Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stunndard committed May 20, 2018
0 parents commit e9cea88
Show file tree
Hide file tree
Showing 1,921 changed files with 692,565 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ethx
ethx.exe
bin
.idea
123 changes: 123 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# ethtx - a microservice to generate Ethereum addressess and sign Ethereum transactions

## What's the point?
`ethtx` is a small, stateless and portable microservice for generating Ethereum addresses and signing Ethereum transactions without the dedicated Ethereum node.
It comes extremely handy when you need to create Ethereum addresses (private/public key pair) or sign Ethereum transactions using a programming language that doesn't have the required crypto libraries.

## How it works?
It works as a HTTP service. You POST parameters and get a JSON response.

## What crypto libraries does ethtx use? Is it safe?
`ethtx` is using `geth`'s implentation directly by importing the corresponding source packages directly from the [go-ethereum](https://github.com/ethereum/go-ethereum) project.

## How it generates the private keys? Any implementation of such crucial part should be seriously reviewed.
`ethtx` derives key generation directly from [go-ethereum](https://github.com/ethereum/go-ethereum) sources. It doesn't add anything else to it. Please check the sources.

## Tell me more.
So far the following methods are supported:

### `generateAddress`
Generates a new Ethereum address (private/public keypair):

##### Input parameters:
`none`

##### Curl example:

```
curl -X POST \
http://ethtx-address:8070/generateAddress
```

##### Response:
```JSON
{
"address": "0x2cBD5C1D45DCcCD5147BED0314379c6b50c3e8a5",
"privKey": "0xc3c4a14597d9ca779f730df5716c58321e46bc55d6559d8ba2932f0da04e1dbe",
"pubKey": "0x7cbd2cd558251149285fc379fecbf030a92be3b6f2c793ae5c3a7dfefe4076da, 0xff5365976d4c8cddb531bf9dcb19963d8e03bebfcac713bca7017886ef5be7ab"
}
```

##### Output:

`address` : a new freshly generated Ethereum address.

`privKey` : the private key for this address. Be careful with it and NEVER save it unencrypted.

`pubKey` : the public key for this address. Consists of two parts, if you need the public key, just concatenate this two parts.

### `signTx`
Signs a transaction and returns it in RLP coding, ready to be sent to the Ethereum network via a full node, or a 3rd party service like [Infura](https://infura.io/) or [Etherscan](https://etherscan.io/apis)

##### Input parameters:

`chainId` : (integer) your network chain ID. Use 1 for the production network and any other custom one for your own private networks.

`privKey` : (string), the private key used to sign this transaction. Do not use `0x` prefix for the private key.

`sendTo` : (string), the Ethereum address to send this transaction to. Use `0x` prefix for the destination address.

`amount` : (float), the amount of `ETH` to send. For example, 23.33323

`amountWei` : (integer), if you don't want to pass the amount in `ETH`, put `0` in `amount` above, and use the amount in `wei` here. For example, 30000000000000000

`gasLimit` : (integer), gas limit for this transaction, for example, 21000

`gasPrice` : (integer), gas price in `wei` for this transaction, for example, 20000000000

`nonce` : (integer), nonce value for this transaction. Use [eth_getTransactionCount](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount) to get the nonce before signing this transaction. Without the valid nonce, your transaction won't be relayed to the network and will be stuck.

##### Curl example:

```
curl -X POST \
http://ethtx-address:8070/signTx \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'chainId=88882&privKey=c3c4a14597d9ca779f730df5716c58321e46bc55d6559d8ba2932f0da04e1dbe&sendTo=0x3822e05392f097cf03ba3227cc16b73ceb0ee305&amount=0.1&gasLimit=21000&gasPrice=20000000000&nonce=1'
```

##### Output:

`result` : if it is `ok` then `signedTx` contains the signed transaction.

`signedTx` : signed transaction ready to be sent to the Ethereum network by any available means.

##### Response:
```JSON
{
"result": "ok",
"signedTx": "0xf86f018504a817c800825208943822e05392f097cf03ba3227cc16b73ceb0ee30588016345785d8a000080830300afa0ab96c871bf77e8f2a67b744fbec7138a45f7a706a3cdd758989f3b49da30299ba05eb9343eb000e620e62c31769d6a5e5ac8c191f3afa2f9c934d541520e4340f9"
}
```

## What platforms are supported?
Any that Go can compile for.

## What is required?
Just Go.

## How do I install goicy?
The `go get` command will automatically fetch all dependencies required, compile the binary and place it in your $GOPATH/bin directory.

go get github.com/stunndard/ethtx

## How do I configure it?
Read `ethtx.yml `. Tune it for your needs.
```YAML
---
Gzip: true
Other:
listen: ":8070"
redactLogs: True

```

`redactLogs` will hide the private keys from the logs. By default `ethtx` will write the logs to the standard output. You can hide that or redirect to files, as usual.

## How do I run it?
Just run the binary
```
./ethtx
```

You can run it inside `tmux` or `screen` or you can write a systemd config for it to run as daemon.
43 changes: 43 additions & 0 deletions ethtx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"github.com/stunndard/ethtx/routes"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)

func startWebApp() {
// load config
app := iris.New(iris.YAML("ethtx.yml"))

routes.RedactLogs = app.Config.Other["redactLogs"].(bool) == true

app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())

// first handlers
app.UseFunc(routes.Log)
app.UseFunc(routes.Headers)

// 404 handler
app.OnError(iris.StatusNotFound, routes.NotFound)

// app handlers
app.Get("/health", routes.ApiHealth)
app.Get("/", routes.ApiVersion)

// sign tx
app.Post("/signTx", routes.CheckTx, routes.SignTx)
// generate address
app.Post("generateAddress", routes.GenerateAddress)

// start the web app
app.Listen(app.Config.Other["listen"].(string))
}

func main() {



startWebApp()
}
6 changes: 6 additions & 0 deletions ethtx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
Gzip: true
Other:
listen: ":8070"
redactLogs: true

41 changes: 41 additions & 0 deletions routes/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package routes

import (
"fmt"
"log"

"github.com/ethereum/go-ethereum/crypto"
"gopkg.in/kataras/iris.v6"
)

func GenerateAddress(ctx *iris.Context) {

privKey, err := crypto.GenerateKey()
if err != nil {
log.Println("error generating private key", err)
renderJSONError(ctx, "101", "error generating key")
return
}

//pubKey :=
address := crypto.PubkeyToAddress(privKey.PublicKey).Hex()

log.Println("key successfully generated")

privKey1 := fmt.Sprintf("0x%x", privKey.D)
if RedactLogs {
log.Println("private key: 0x...")
} else {
log.Println("private key:", privKey1)
}

pubKey1 := fmt.Sprintf("0x%x, 0x%x", privKey.PublicKey.X, privKey.PublicKey.Y)
log.Println("public key:", pubKey1)
log.Println("address :", address)

ctx.JSON(iris.StatusOK, &iris.Map{
"privKey": privKey1,
"pubKey": pubKey1,
"address": address,
})
}
27 changes: 27 additions & 0 deletions routes/ethtx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package routes

import (
"time"

"gopkg.in/kataras/iris.v6"
)

var (
Started time.Time
Version string
)

// show health
func ApiHealth(ctx *iris.Context) {
ctx.JSON(iris.StatusOK, &iris.Map{"health": "OK"})
}

// show version
func ApiVersion(ctx *iris.Context) {
ctx.JSON(iris.StatusOK, &iris.Map{
"success": true,
"status": 200,
"version": Version,
"started": Started,
})
}
43 changes: 43 additions & 0 deletions routes/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package routes

import (
"log"
"time"

"gopkg.in/kataras/iris.v6"
)

var RedactLogs bool = false

func Headers(ctx *iris.Context) {
ctx.SetHeader("Server", "X-Files")
ctx.Next()
}

func Log(ctx *iris.Context) {
log.Println("[B]", ctx.RemoteAddr(), ctx.Method(), ctx.Path())
timeStarted := time.Now()
ctx.Next()
timeElapsed := time.Now().Sub(timeStarted)

log.Println("[E]", ctx.RemoteAddr(), ctx.Method(), ctx.StatusCode(), ctx.Path(), timeElapsed)
log.Println("---")
}

func NotFound(ctx *iris.Context ) {
log.Println(ctx.RemoteAddr(), ctx.Method(), ctx.StatusCode(), ctx.Path())
renderJSONError(ctx, "404", "not found")
}

func renderJSONError(ctx *iris.Context, code, message string) {
ctx.JSON(
iris.StatusOK,
&iris.Map{
"error": &iris.Map{
"code": code,
"message": message,
},
},
)
ctx.StopExecution()
}
Loading

0 comments on commit e9cea88

Please sign in to comment.