signer: refactoring + documentation

This commit is contained in:
Martin Holst Swende 2018-02-15 11:46:04 +01:00
parent a8e68b9cc3
commit 99dd0a1b58
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
12 changed files with 175 additions and 122 deletions

View file

@ -174,9 +174,9 @@ None
Signs a transactions and responds with the signed transaction in RLP encoded form. Signs a transactions and responds with the signed transaction in RLP encoded form.
#### Arguments #### Arguments
1. from [address]: account to send the transaction from
2. transaction object: 2. transaction object:
- `to` [address]: receiver account - `from` [address]: account to send the transaction from
- `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
- `gas` [number]: maximum amount of gas to burn - `gas` [number]: maximum amount of gas to burn
- `gasPrice` [number]: gas price - `gasPrice` [number]: gas price
- `value` [number:optional]: amount of Wei to send with the transaction - `value` [number:optional]: amount of Wei to send with the transaction
@ -196,8 +196,8 @@ None
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "account_signTransaction", "method": "account_signTransaction",
"params": [ "params": [
"0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
{ {
"from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"gas": "0x55555", "gas": "0x55555",
"gasPrice": "0x1234", "gasPrice": "0x1234",
"input": "0xabcd", "input": "0xabcd",
@ -209,11 +209,64 @@ None
} }
{ {
"id": 2,
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": "0xf86480821234830555559407a565b7ed7d7a678680a4c162885bedbb695fe0821234802ea028f9ebeff90732eae45692a11c4ca2ef7f631a0a25bf8763d093e770c4ec464aa01fae77b24617913e718b989be78bc1aabb2fed3f2d4e3b93bd36759f1b5b4904" "id": 67,
"error": {
"code": -32000,
"message": "Request denied"
}
} }
``` ```
#### Sample call with ABI-data
```
{
"jsonrpc": "2.0",
"method": "account_signTransaction",
"params": [
{
"from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa",
"gas": "0x333",
"gasPrice": "0x1",
"nonce": "0x0",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x0",
"data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"
},
"safeSend(address)"
],
"id": 67
}
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"tx": {
"nonce": "0x0",
"gasPrice": "0x1",
"gas": "0x333",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x0",
"input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
"v": "0x26",
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
}
}
}
```
Bash example:
```bash
#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
```
### account_sign ### account_sign
@ -395,7 +448,8 @@ By starting the signer with the switch `--stdio-ui-test`, the signer will invoke
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
**work in progress** All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key.
### ApproveTx ### ApproveTx
@ -560,6 +614,15 @@ The UI should show the info to the user. Does not expect response.
``` ```
### OnApproved
`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
When implementing rate-limited rules, this callback should be used.
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
### Rules for UI apis ### Rules for UI apis
A UI should conform to the following rules. A UI should conform to the following rules.

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
// "bytes" // "bytes"
@ -157,25 +157,25 @@ func MethodSelectorToAbi(selector string) ([]byte, error) {
} }
type abiDb struct { type AbiDb struct {
db map[string]string db map[string]string
} }
// NewAbiDBFromFile loads signature database from file, and // NewAbiDBFromFile loads signature database from file, and
// errors if the file is not valid json. Does no other validation of contents // errors if the file is not valid json. Does no other validation of contents
func NewAbiDBFromFile(path string) (*abiDb, error) { func NewAbiDBFromFile(path string) (*AbiDb, error) {
raw, err := ioutil.ReadFile(path) raw, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
db := new(abiDb) db := new(AbiDb)
json.Unmarshal(raw, &db.db) json.Unmarshal(raw, &db.db)
return db, nil return db, nil
} }
// LookupMethodSelector checks the given 4byte-sequence against the known ABI methods. // LookupMethodSelector checks the given 4byte-sequence against the known ABI methods.
// OBS: This method does not validate the match, it's assumed the caller will do so // OBS: This method does not validate the match, it's assumed the caller will do so
func (db *abiDb) LookupMethodSelector(id []byte) (string, error) { func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) {
if len(id) != 4 { if len(id) != 4 {
return "", fmt.Errorf("Expected 4-byte id, got %d", len(id)) return "", fmt.Errorf("Expected 4-byte id, got %d", len(id))
} }
@ -185,6 +185,6 @@ func (db *abiDb) LookupMethodSelector(id []byte) (string, error) {
} }
return "", fmt.Errorf("Signature %v not found", sig) return "", fmt.Errorf("Signature %v not found", sig)
} }
func (db *abiDb) Size() int { func (db *AbiDb) Size() int {
return len(db.db) return len(db.db)
} }

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
"fmt" "fmt"
@ -176,13 +176,13 @@ func TestCalldataDecoding(t *testing.T) {
func TestSelectorUnmarshalling(t *testing.T) { func TestSelectorUnmarshalling(t *testing.T) {
var ( var (
db *abiDb db *AbiDb
err error err error
abistring []byte abistring []byte
abistruct abi.ABI abistruct abi.ABI
) )
db, err = NewAbiDBFromFile("4byte.json") db, err = NewAbiDBFromFile("../4byte.json")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
"context" "context"
@ -69,7 +69,7 @@ type SignerUI interface {
// ApproveImport prompt the user for confirmation to import Account json // ApproveImport prompt the user for confirmation to import Account json
ApproveImport(request *ImportRequest) (ImportResponse, error) ApproveImport(request *ImportRequest) (ImportResponse, error)
// ApproveListing prompt the user for confirmation to list accounts // ApproveListing prompt the user for confirmation to list accounts
// the list of accounts to list can be modified by the ui // the list of accounts to list can be modified by the UI
ApproveListing(request *ListRequest) (ListResponse, error) ApproveListing(request *ListRequest) (ListResponse, error)
// ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller // ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller
ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error)
@ -86,8 +86,8 @@ type SignerUI interface {
type SignerAPI struct { type SignerAPI struct {
chainID *big.Int chainID *big.Int
am *accounts.Manager am *accounts.Manager
ui SignerUI UI SignerUI
abidb abiDb abidb AbiDb
} }
// Metadata about a request // Metadata about a request
@ -201,7 +201,7 @@ func (ew errorWrapper) String() string {
// key that is generated when a new Account is created. // key that is generated when a new Account is created.
// noUSB disables USB support that is required to support hardware devices such as // noUSB disables USB support that is required to support hardware devices such as
// ledger and trezor. // ledger and trezor.
func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *abiDb, lightKDF bool) *SignerAPI { func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *AbiDb, lightKDF bool) *SignerAPI {
var ( var (
backends []accounts.Backend backends []accounts.Backend
n, p = keystore.StandardScryptN, keystore.StandardScryptP n, p = keystore.StandardScryptN, keystore.StandardScryptP
@ -242,7 +242,7 @@ func (api *SignerAPI) List(ctx context.Context) (Accounts, error) {
accs = append(accs, acc) accs = append(accs, acc)
} }
} }
result, err := api.ui.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)}) result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -261,7 +261,7 @@ func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) {
if len(be) == 0 { if len(be) == 0 {
return accounts.Account{}, errors.New("password based accounts not supported") return accounts.Account{}, errors.New("password based accounts not supported")
} }
resp, err := api.ui.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}) resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)})
if err != nil { if err != nil {
return accounts.Account{}, err return accounts.Account{}, err
@ -277,34 +277,26 @@ func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) {
// UI-modifications to requests // UI-modifications to requests
func logDiff(original *SignTxRequest, new *SignTxResponse) bool { func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
modified := false modified := false
if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) { if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) {
log.Info("Sender-account changed by UI", "was", f0, "is", f1) log.Info("Sender-account changed by UI", "was", f0, "is", f1)
modified = true modified = true
} }
if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) { if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) {
log.Info("Recipient-account changed by UI", "was", t0, "is", t1) log.Info("Recipient-account changed by UI", "was", t0, "is", t1)
modified = true modified = true
} }
if g0, g1 := (*big.Int)(original.Transaction.Gas), (*big.Int)(new.Transaction.Gas); g0 != g1 { if g0, g1 := big.Int(original.Transaction.Gas), big.Int(new.Transaction.Gas); g0.Cmp(&g1) != 0 {
if g0 == nil || g1 == nil || g0.Cmp(g1) != 0 {
modified = true modified = true
log.Info("Gas changed by UI", "was", g0, "is", g1) log.Info("Gas changed by UI", "was", g0, "is", g1)
} }
} if g0, g1 := big.Int(original.Transaction.GasPrice), big.Int(new.Transaction.GasPrice); g0.Cmp(&g1) != 0 {
if g0, g1 := (*big.Int)(original.Transaction.GasPrice), (*big.Int)(new.Transaction.GasPrice); g0 != g1 {
if g0 == nil || g1 == nil || g0.Cmp(g1) != 0 {
modified = true modified = true
log.Info("GasPrice changed by UI", "was", g0, "is", g1) log.Info("GasPrice changed by UI", "was", g0, "is", g1)
} }
} if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 {
if v0, v1 := (*big.Int)(original.Transaction.Value), (*big.Int)(new.Transaction.Value); v0 != v1 {
if v0 == nil || v1 == nil || v0.Cmp(v1) != 0 {
modified = true modified = true
log.Info("Value changed by UI", "was", v0, "is", v1) log.Info("Value changed by UI", "was", v0, "is", v1)
} }
}
if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 {
d0s := "" d0s := ""
d1s := "" d1s := ""
@ -320,12 +312,9 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
} }
} }
if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 {
if n0 == nil || n1 == nil || (*n0) != (*n1) {
modified = true modified = true
log.Info("Nonce changed by UI", "was", n0, "is", n1) log.Info("Nonce changed by UI", "was", n0, "is", n1)
} }
}
return modified return modified
} }
@ -391,7 +380,7 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
Callinfo: api.determineCallInfo(data, methodSelector), Callinfo: api.determineCallInfo(data, methodSelector),
} }
// Process approval // Process approval
result, err = api.ui.ApproveTx(&req) result, err = api.UI.ApproveTx(&req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -415,7 +404,7 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
// The one to sign is the one that was returned from the UI // The one to sign is the one that was returned from the UI
signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID) signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID)
if err != nil { if err != nil {
api.ui.ShowError(err.Error()) api.UI.ShowError(err.Error())
return nil, err return nil, err
} }
@ -423,7 +412,7 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
response := ethapi.SignTransactionResult{rlpdata, signedTx} response := ethapi.SignTransactionResult{rlpdata, signedTx}
// Finally, send the signed tx to the UI // Finally, send the signed tx to the UI
api.ui.OnApprovedTx(response) api.UI.OnApprovedTx(response)
// ...and to the external caller // ...and to the external caller
return &response, nil return &response, nil
@ -443,7 +432,7 @@ func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, da
// We make the request prior to looking up if we actually have the account, to prevent // We make the request prior to looking up if we actually have the account, to prevent
// account-enumeration via the API // account-enumeration via the API
req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)} req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)}
res, err := api.ui.ApproveSignData(req) res, err := api.UI.ApproveSignData(req)
if err != nil { if err != nil {
return nil, err return nil, err
@ -460,7 +449,7 @@ func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, da
// Assemble sign the data with the wallet // Assemble sign the data with the wallet
signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash) signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash)
if err != nil { if err != nil {
api.ui.ShowError(err.Error()) api.UI.ShowError(err.Error())
return nil, err return nil, err
} }
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
@ -509,7 +498,7 @@ func signHash(data []byte) ([]byte, string) {
// Export returns encrypted private key associated with the given address in web3 keystore format. // Export returns encrypted private key associated with the given address in web3 keystore format.
func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
res, err := api.ui.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)}) res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)})
if err != nil { if err != nil {
return nil, err return nil, err
@ -537,7 +526,7 @@ func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Acco
if len(be) == 0 { if len(be) == 0 {
return Account{}, errors.New("password based accounts not supported") return Account{}, errors.New("password based accounts not supported")
} }
res, err := api.ui.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)}) res, err := api.UI.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)})
if err != nil { if err != nil {
return Account{}, err return Account{}, err
@ -547,7 +536,7 @@ func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Acco
} }
acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword) acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword)
if err != nil { if err != nil {
api.ui.ShowError(err.Error()) api.UI.ShowError(err.Error())
return Account{}, err return Account{}, err
} }
return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil

View file

@ -1,4 +1,4 @@
package signer package core
import ( import (
"bytes" "bytes"
@ -35,9 +35,9 @@ func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error)
case "Y": case "Y":
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
case "M": //Modify case "M": //Modify
old := (*big.Int)(request.Transaction.Value) old := big.Int(request.Transaction.Value)
newVal := big.NewInt(0).Add(old, big.NewInt(1)) newVal := big.NewInt(0).Add(&old, big.NewInt(1))
request.Transaction.Value = (*hexutil.Big)(newVal) request.Transaction.Value = hexutil.Big(*newVal)
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
default: default:
return SignTxResponse{request.Transaction, false, ""}, nil return SignTxResponse{request.Transaction, false, ""}, nil
@ -106,7 +106,7 @@ func setup(t *testing.T) (*SignerAPI, chan string) {
controller := make(chan string, 10) controller := make(chan string, 10)
db, err := NewAbiDBFromFile(fmt.Sprintf("./4byte.json")) db, err := NewAbiDBFromFile(fmt.Sprintf("../4byte.json"))
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
@ -238,9 +238,9 @@ func TestSignData(t *testing.T) {
} }
func mkTestTx(from common.MixedcaseAddress) SendTxArgs { func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
gas := (*hexutil.Big)(big.NewInt(21000)) gas := (hexutil.Big)(*big.NewInt(21000))
gasPrice := (*hexutil.Big)(big.NewInt(2000000000)) gasPrice := (hexutil.Big)(*big.NewInt(2000000000))
value := (*hexutil.Big)(big.NewInt(1e18)) value := (hexutil.Big)(*big.NewInt(1e18))
nonce := (hexutil.Uint64)(0) nonce := (hexutil.Uint64)(0)
data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
tx := SendTxArgs{ tx := SendTxArgs{
@ -250,7 +250,7 @@ func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
GasPrice: gasPrice, GasPrice: gasPrice,
Value: value, Value: value,
Data: &data, Data: &data,
Nonce: &nonce} Nonce: nonce}
return tx return tx
} }

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
"context" "context"

View file

@ -13,7 +13,7 @@
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
"bufio" "bufio"
@ -181,7 +181,7 @@ func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse,
} }
// ApproveListing prompt the user for confirmation to list accounts // ApproveListing prompt the user for confirmation to list accounts
// the list of accounts to list can be modified by the ui // the list of accounts to list can be modified by the UI
func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) { func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) {
ui.mu.Lock() ui.mu.Lock()

View file

@ -15,7 +15,7 @@
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// //
package signer package core
import ( import (
"context" "context"

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package signer package core
import ( import (
"encoding/json" "encoding/json"
@ -66,10 +66,10 @@ type TransactionArg struct {
type SendTxArgs struct { type SendTxArgs struct {
From common.MixedcaseAddress `json:"from"` From common.MixedcaseAddress `json:"from"`
To *common.MixedcaseAddress `json:"to"` To *common.MixedcaseAddress `json:"to"`
Gas *hexutil.Big `json:"gas"` Gas hexutil.Big `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"` GasPrice hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"` Value hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"` Nonce hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons. // We accept "data" and "input" for backwards-compatibility reasons.
Data *hexutil.Bytes `json:"data"` Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"` Input *hexutil.Bytes `json:"input"`
@ -91,7 +91,7 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
input = *args.Input input = *args.Input
} }
if args.To == nil { if args.To == nil {
return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), input) return types.NewContractCreation(uint64(args.Nonce), (*big.Int)(&args.Value), (*big.Int)(&args.Gas), (*big.Int)(&args.GasPrice), input)
} }
return types.NewTransaction(uint64(*args.Nonce), (*args.To).Address(), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), input) return types.NewTransaction(uint64(args.Nonce), args.To.Address(), (*big.Int)(&args.Value), (*big.Int)(&args.Gas), (*big.Int)(&args.GasPrice), input)
} }

View file

@ -16,7 +16,7 @@
// signer is a utility that can be used so sign transactions and // signer is a utility that can be used so sign transactions and
// arbitrary data. // arbitrary data.
package signer package main
import ( import (
"fmt" "fmt"
@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/cmd/signer/core"
) )
func main() { func main() {
@ -90,7 +91,7 @@ func main() {
app.Action = func(c *cli.Context) error { app.Action = func(c *cli.Context) error {
var ( var (
ui SignerUI ui core.SignerUI
) )
// Set up the logger to print everything // Set up the logger to print everything
logOutput := os.Stdout logOutput := os.Stdout
@ -100,14 +101,14 @@ func main() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(logOutput, log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(logOutput, log.TerminalFormat(true))))
if c.Bool("stdio-ui") { if c.Bool("stdio-ui") {
ui = NewStdIOUI() ui = core.NewStdIOUI()
} else { } else {
ui = NewCommandlineUI() ui = core.NewCommandlineUI()
} }
if c.Bool("stdio-ui") { if c.Bool("stdio-ui") {
log.Info("Using stdin/stdout as UI-channel") log.Info("Using stdin/stdout as UI-channel")
} }
db, err := NewAbiDBFromFile(c.String("4bytedb")) db, err := core.NewAbiDBFromFile(c.String("4bytedb"))
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
@ -115,12 +116,12 @@ func main() {
log.Info("Loaded 4byte db", "signatures", db.Size(), "file", c.String("4bytedb")) log.Info("Loaded 4byte db", "signatures", db.Size(), "file", c.String("4bytedb"))
var ( var (
api ExternalAPI api core.ExternalAPI
listener net.Listener listener net.Listener
server = rpc.NewServer() server = rpc.NewServer()
) )
api_impl := NewSignerAPI( api_impl := core.NewSignerAPI(
c.Int64(utils.NetworkIdFlag.Name), c.Int64(utils.NetworkIdFlag.Name),
c.String("keystore"), c.String("keystore"),
c.Bool(utils.NoUSBFlag.Name), c.Bool(utils.NoUSBFlag.Name),
@ -131,7 +132,7 @@ func main() {
// Audit logging // Audit logging
if logfile := c.String("auditlog"); logfile != "" { if logfile := c.String("auditlog"); logfile != "" {
api, err = NewAuditLogger(logfile, api_impl) api, err = core.NewAuditLogger(logfile, api_impl)
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
} }
@ -170,7 +171,7 @@ func main() {
} }
func testExternalUI(api *SignerAPI) { func testExternalUI(api *core.SignerAPI) {
ctx := context.WithValue(context.Background(), "remote", "signer binary") ctx := context.WithValue(context.Background(), "remote", "signer binary")
ctx = context.WithValue(ctx, "scheme", "in-proc") ctx = context.WithValue(ctx, "scheme", "in-proc")
@ -178,17 +179,17 @@ func testExternalUI(api *SignerAPI) {
errs := make([]string, 0) errs := make([]string, 0)
api.ui.ShowInfo("Testing 'ShowInfo'") api.UI.ShowInfo("Testing 'ShowInfo'")
api.ui.ShowError("Testing 'ShowError'") api.UI.ShowError("Testing 'ShowError'")
checkErr := func(method string, err error) { checkErr := func(method string, err error) {
if err != nil && err != ErrRequestDenied { if err != nil && err != core.ErrRequestDenied {
errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error())) errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error()))
} }
} }
var err error var err error
_, err = api.SignTransaction(ctx, SendTxArgs{From:common.MixedcaseAddress{}}, nil) _, err = api.SignTransaction(ctx, core.SendTxArgs{From:common.MixedcaseAddress{}}, nil)
checkErr("SignTransaction", err) checkErr("SignTransaction", err)
_, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304")) _, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
checkErr("Sign", err) checkErr("Sign", err)
@ -201,7 +202,7 @@ func testExternalUI(api *SignerAPI) {
_, err = api.Import(ctx, json.RawMessage{}) _, err = api.Import(ctx, json.RawMessage{})
checkErr("Import", err) checkErr("Import", err)
api.ui.ShowInfo("Tests completed") api.UI.ShowInfo("Tests completed")
if len(errs) > 0 { if len(errs) > 0 {
log.Error("Got errors") log.Error("Got errors")

View file

@ -19,7 +19,6 @@ package rules
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/cmd/signer"
"github.com/ethereum/go-ethereum/cmd/signer/rules/deps" "github.com/ethereum/go-ethereum/cmd/signer/rules/deps"
"github.com/ethereum/go-ethereum/cmd/signer/storage" "github.com/ethereum/go-ethereum/cmd/signer/storage"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
@ -27,6 +26,7 @@ import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"os" "os"
"strings" "strings"
"github.com/ethereum/go-ethereum/cmd/signer/core"
) )
var ( var (
@ -48,7 +48,7 @@ func consoleOutput(call otto.FunctionCall) otto.Value {
// file for each defined UI-method // file for each defined UI-method
type rulesetUi struct { type rulesetUi struct {
vm *otto.Otto // The JS vm vm *otto.Otto // The JS vm
next signer.SignerUI // The next handler, for manual processing next core.SignerUI // The next handler, for manual processing
storage storage.Storage storage storage.Storage
} }
@ -104,45 +104,45 @@ func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) error
return fmt.Errorf("rejected") return fmt.Errorf("rejected")
} }
func (r *rulesetUi) ApproveTx(request *signer.SignTxRequest) (signer.SignTxResponse, error) { func (r *rulesetUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
jsonreq, err := json.Marshal(request) jsonreq, err := json.Marshal(request)
if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil {
return signer.SignTxResponse{Transaction: request.Transaction, Approved: true, Password: ""}, nil return core.SignTxResponse{Transaction: request.Transaction, Approved: true, Password: ""}, nil
} }
return signer.SignTxResponse{Approved: false}, err return core.SignTxResponse{Approved: false}, err
} }
func (r *rulesetUi) ApproveSignData(request *signer.SignDataRequest) (signer.SignDataResponse, error) { func (r *rulesetUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
jsonreq, err := json.Marshal(request) jsonreq, err := json.Marshal(request)
if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil {
return signer.SignDataResponse{Approved: true, Password: ""}, nil return core.SignDataResponse{Approved: true, Password: ""}, nil
} }
return signer.SignDataResponse{Approved: false, Password: ""}, err return core.SignDataResponse{Approved: false, Password: ""}, err
} }
func (r *rulesetUi) ApproveExport(request *signer.ExportRequest) (signer.ExportResponse, error) { func (r *rulesetUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
jsonreq, err := json.Marshal(request) jsonreq, err := json.Marshal(request)
if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil {
return signer.ExportResponse{Approved: true}, nil return core.ExportResponse{Approved: true}, nil
} }
return signer.ExportResponse{Approved: false}, err return core.ExportResponse{Approved: false}, err
} }
func (r *rulesetUi) ApproveImport(request *signer.ImportRequest) (signer.ImportResponse, error) { func (r *rulesetUi) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
// This cannot be handled by rules, requires setting a password // This cannot be handled by rules, requires setting a password
// dispatch to next // dispatch to next
return r.next.ApproveImport(request) return r.next.ApproveImport(request)
} }
func (r *rulesetUi) ApproveListing(request *signer.ListRequest) (signer.ListResponse, error) { func (r *rulesetUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
jsonreq, err := json.Marshal(request) jsonreq, err := json.Marshal(request)
if err = r.checkApproval("ApproveListing", jsonreq, err); err == nil { if err = r.checkApproval("ApproveListing", jsonreq, err); err == nil {
return signer.ListResponse{Accounts: request.Accounts}, nil return core.ListResponse{Accounts: request.Accounts}, nil
} }
return signer.ListResponse{}, err return core.ListResponse{}, err
} }
func (r *rulesetUi) ApproveNewAccount(request *signer.NewAccountRequest) (signer.NewAccountResponse, error) { func (r *rulesetUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
// This cannot be handled by rules, requires setting a password // This cannot be handled by rules, requires setting a password
// dispatch to next // dispatch to next
return r.next.ApproveNewAccount(request) return r.next.ApproveNewAccount(request)

View file

@ -3,13 +3,13 @@ package rules
import ( import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/signer"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/cmd/signer/core"
) )
const JS = ` const JS = `
@ -66,11 +66,11 @@ func initRuleEngine(js string) (*rulesetUi, error) {
} }
func TestListRequest(t *testing.T) { func TestListRequest(t *testing.T) {
accs := make([]signer.Account, 5) accs := make([]core.Account, 5)
for i, _ := range accs { for i, _ := range accs {
addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i) addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i)
acc := signer.Account{ acc := core.Account{
Address: common.BytesToAddress(common.Hex2Bytes(addr)), Address: common.BytesToAddress(common.Hex2Bytes(addr)),
URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)}, URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)},
} }
@ -84,9 +84,9 @@ func TestListRequest(t *testing.T) {
t.Errorf("Couldn't create evaluator %v", err) t.Errorf("Couldn't create evaluator %v", err)
return return
} }
resp, err := r.ApproveListing(&signer.ListRequest{ resp, err := r.ApproveListing(&core.ListRequest{
accs, accs,
signer.Metadata{ core.Metadata{
"remoteip", "localip", "inproc", "remoteip", "localip", "inproc",
}, },
}) })
@ -126,12 +126,12 @@ func TestSignTxRequest(t *testing.T) {
return return
} }
fmt.Printf("to %v", to.Address().String()) fmt.Printf("to %v", to.Address().String())
resp, err := r.ApproveTx(&signer.SignTxRequest{ resp, err := r.ApproveTx(&core.SignTxRequest{
Transaction: signer.SendTxArgs{ Transaction: core.SendTxArgs{
From: *from, From: *from,
To: to}, To: to},
Callinfo: "", Callinfo: "",
Meta: signer.Metadata{"remoteip", "localip", "inproc"}, Meta: core.Metadata{"remoteip", "localip", "inproc"},
}) })
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
@ -290,7 +290,7 @@ const ExampleTxWindow = `
` `
func dummyTx(value *hexutil.Big) *signer.SignTxRequest { func dummyTx(value hexutil.Big) *core.SignTxRequest {
to, _ := mixAddr("000000000000000000000000000000000000dead") to, _ := mixAddr("000000000000000000000000000000000000dead")
from, _ := mixAddr("000000000000000000000000000000000000dead") from, _ := mixAddr("000000000000000000000000000000000000dead")
@ -298,17 +298,17 @@ func dummyTx(value *hexutil.Big) *signer.SignTxRequest {
gas := hexutil.Big(*big.NewInt(21000)) gas := hexutil.Big(*big.NewInt(21000))
gasPrice := hexutil.Big(*big.NewInt(2000000)) gasPrice := hexutil.Big(*big.NewInt(2000000))
return &signer.SignTxRequest{ return &core.SignTxRequest{
Transaction: signer.SendTxArgs{ Transaction: core.SendTxArgs{
From: *from, From: *from,
To: to, To: to,
Value: value, Value: value,
Nonce: &n, Nonce: n,
GasPrice: &gas, GasPrice: gas,
Gas: &gasPrice, Gas: gasPrice,
}, },
Callinfo: "Warning, all your base are bellong to us", Callinfo: "Warning, all your base are bellong to us",
Meta: signer.Metadata{"remoteip", "localip", "inproc"}, Meta: core.Metadata{"remoteip", "localip", "inproc"},
} }
} }
func dummySigned(value *big.Int) *types.Transaction { func dummySigned(value *big.Int) *types.Transaction {
@ -335,7 +335,7 @@ func TestLimitWindow(t *testing.T) {
h := hexutil.Big(*v) h := hexutil.Big(*v)
// The first three should succeed // The first three should succeed
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
unsigned := dummyTx(&h) unsigned := dummyTx(h)
resp, err := r.ApproveTx(unsigned) resp, err := r.ApproveTx(unsigned)
if err != nil { if err != nil {
t.Errorf("Unexpected error %v", err) t.Errorf("Unexpected error %v", err)
@ -352,7 +352,7 @@ func TestLimitWindow(t *testing.T) {
r.OnApprovedTx(response) r.OnApprovedTx(response)
} }
// Fourth should fail // Fourth should fail
resp, err := r.ApproveTx(dummyTx(&h)) resp, err := r.ApproveTx(dummyTx(h))
if resp.Approved { if resp.Approved {
t.Errorf("Expected check to resolve to 'Reject'") t.Errorf("Expected check to resolve to 'Reject'")
} }