From be24e89a8fe8e6f8d0b1c73599747f10faf98d8c Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 16 Feb 2018 12:45:51 +0100 Subject: [PATCH] signer: implement validation rules, change signature of call_info --- cmd/signer/README.md | 148 ++++++++++++++++++++++++---- cmd/signer/core/abihelper.go | 5 + cmd/signer/core/api.go | 72 +++----------- cmd/signer/core/cliui.go | 11 ++- cmd/signer/core/stdioui.go | 9 +- cmd/signer/core/types.go | 8 ++ cmd/signer/core/validation.go | 152 +++++++++++++++++++++++++++++ cmd/signer/core/validation_test.go | 140 ++++++++++++++++++++++++++ cmd/signer/rules/rules_test.go | 19 ++-- 9 files changed, 470 insertions(+), 94 deletions(-) create mode 100644 cmd/signer/core/validation.go create mode 100644 cmd/signer/core/validation_test.go diff --git a/cmd/signer/README.md b/cmd/signer/README.md index 25db2c9ee4..66490aaf74 100644 --- a/cmd/signer/README.md +++ b/cmd/signer/README.md @@ -506,29 +506,137 @@ Invoked when there's a transaction for approval. #### Sample call +Here's a method invocation: +```bash + +curl -i -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/ +``` + ```json { - "jsonrpc": "2.0", - "method": "ApproveTx", - "params": [{ - "transaction": { - "to": "0xae967917c465db8578ca9024c205720b1a3651A9", - "gas": "0x333", - "gasPrice": "0x123", - "value": "0x10", - "data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff", - "nonce": "0x0" - }, - "fromaccount": "0xAe967917c465db8578ca9024c205720b1a3651A9", - "call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658", - "meta": { - "remote": "127.0.0.1:34572", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - }], - "id": 1 + "jsonrpc": "2.0", + "id": 1, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": { + "Messages": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "Info", + "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" + } + ] + }, + "meta": { + "remote": "127.0.0.1:48486", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + +``` + +The same method invocation, but with invalid data: +```bash + +curl -i -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":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/ +``` + +```json + +{ + "jsonrpc": "2.0", + "id": 1, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", + "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", + "gas": "0x333", + "gasPrice": "0x1", + "value": "0x0", + "nonce": "0x0", + "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", + "input": null + }, + "call_info": { + "Messages": [ + { + "type": "WARNING", + "message": "Invalid checksum on to-address" + }, + { + "type": "WARNING", + "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" + } + ] + }, + "meta": { + "remote": "127.0.0.1:48492", + "local": "localhost:8550", + "scheme": "HTTP/1.1" + } + } + ] +} + + +``` + +One which has missing `to`, but with no `data`: + + +```json + +{ + "jsonrpc": "2.0", + "id": 3, + "method": "ApproveTx", + "params": [ + { + "transaction": { + "from": "", + "to": null, + "gas": "0x0", + "gasPrice": "0x0", + "value": "0x0", + "nonce": "0x0", + "data": null, + "input": null + }, + "call_info": { + "Messages": [ + { + "type": "CRITICAL", + "message": "Tx will create contract with empty code!" + } + ] + }, + "meta": { + "remote": "signer binary", + "local": "main", + "scheme": "in-proc" + } + } + ] } ``` diff --git a/cmd/signer/core/abihelper.go b/cmd/signer/core/abihelper.go index 625d5cbdf9..61dea02ebd 100644 --- a/cmd/signer/core/abihelper.go +++ b/cmd/signer/core/abihelper.go @@ -161,6 +161,11 @@ type AbiDb struct { db map[string]string } +// NewEmptyAbiDB exists for test purposes +func NewEmptyAbiDB() (*AbiDb, error) { + return &AbiDb{make(map[string]string)}, nil +} + // NewAbiDBFromFile loads signature database from file, and // errors if the file is not valid json. Does no other validation of contents func NewAbiDBFromFile(path string) (*AbiDb, error) { diff --git a/cmd/signer/core/api.go b/cmd/signer/core/api.go index 8e9f4c2d3b..6b96bae094 100644 --- a/cmd/signer/core/api.go +++ b/cmd/signer/core/api.go @@ -23,9 +23,6 @@ import ( "fmt" "io/ioutil" "math/big" - - "bytes" - "reflect" "github.com/ethereum/go-ethereum/accounts" @@ -84,10 +81,10 @@ type SignerUI interface { // SignerAPI defines the actual implementation of ExternalAPI type SignerAPI struct { - chainID *big.Int - am *accounts.Manager - UI SignerUI - abidb AbiDb + chainID *big.Int + am *accounts.Manager + UI SignerUI + validator *Validator } // Metadata about a request @@ -126,9 +123,9 @@ func (m Metadata) String() string { type ( // SignTxRequest contains info about a Transaction to sign SignTxRequest struct { - Transaction SendTxArgs `json:"transaction"` - Callinfo string `json:"call_info"` - Meta Metadata `json:"meta"` + Transaction SendTxArgs `json:"transaction"` + Callinfo *ValidationMessages `json:"call_info"` + Meta Metadata `json:"meta"` } // SignTxResponse result from SignTxRequest SignTxResponse struct { @@ -229,7 +226,7 @@ func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abi log.Debug("Trezor support enabled") } } - return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, *abidb} + return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, NewValidator(abidb)} } // List returns the set of wallet this signer manages. Each wallet can contain @@ -318,66 +315,21 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool { return modified } -// determineCallInfo turns ABI-data + methodselector (if given) into a string suitable -// to present to the user. -func (api *SignerAPI) determineCallInfo(data []byte, methodSelector *string) string { - - if len(data) < 4 { - return "" - } - var ( - selector string - err error - ) - // Try to make sense of the data - if methodSelector == nil { - selector, err = api.abidb.LookupMethodSelector(data[:4]) - if err != nil { - return errorWrapper{"Warning! Could not locate ABI", err}.String() - } - } else { - selector = *methodSelector - } - if selector != "" { - abiData, err := MethodSelectorToAbi(selector) - if err != nil { - return errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String() - } else { - var info *decodedCallData - info, err = parseCallData(data, string(abiData)) - if err != nil { - return errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String() - } else { - return info.String() - } - } - } - return "" -} - // SignTransaction signs the given Transaction and returns it both as json and rlp-encoded form func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { var ( err error result SignTxResponse - data []byte ) - // Prevent accidental erroneous usage of both 'input' and 'data' - if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { - return nil, errors.New(`Ambiguous request: moth "data" and "input" are set and are not identical`) + msgs, err:= api.validator.ValidateTransaction(&args, methodSelector) + if err != nil { + return nil, err } - if args.Data != nil { - data = *args.Data - } else if args.Input != nil { - data = *args.Input - *args.Data = data - *args.Input = nil - } req := SignTxRequest{ Transaction: args, Meta: MetadataFromContext(ctx), - Callinfo: api.determineCallInfo(data, methodSelector), + Callinfo: msgs, } // Process approval result, err = api.UI.ApproveTx(&req) diff --git a/cmd/signer/core/cliui.go b/cmd/signer/core/cliui.go index de4ab466ca..11e85a1bcb 100644 --- a/cmd/signer/core/cliui.go +++ b/cmd/signer/core/cliui.go @@ -92,7 +92,7 @@ func (ui *CommandlineUI) confirm() bool { } func showMetadata(metadata Metadata) { - fmt.Printf("Request info:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local) + fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local) } // ApproveTx prompt the user for confirmation to request to sign Transaction @@ -117,9 +117,12 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro fmt.Printf("data: %v\n", common.Bytes2Hex(d)) } } - if request.Callinfo != "" { - fmt.Printf("\nNote: This Transaction contains data. Review abi-decoding info below:") - fmt.Printf("\nCall info:\n\t%v\n", request.Callinfo) + if request.Callinfo != nil { + fmt.Printf("\nTransaction validation:\n") + for _,m := range request.Callinfo.Messages{ + fmt.Printf(" * %s : %s", m.Typ, m.Message) + } + fmt.Println() } fmt.Printf("\n") diff --git a/cmd/signer/core/stdioui.go b/cmd/signer/core/stdioui.go index a42ff845a5..26818d7f66 100644 --- a/cmd/signer/core/stdioui.go +++ b/cmd/signer/core/stdioui.go @@ -44,8 +44,13 @@ func NewStdIOUI() *StdIOUI { } // dispatch sends a request over the stdio -func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error { - err := ui.client.Call(&reply, serviceMethod, args) +func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error{ + var err error + if reply != nil{ + err = ui.client.Call(nil, serviceMethod, args) + }else{ + err = ui.client.Call(&reply, serviceMethod, args) + } if err != nil { log.Info("Error", "exc", err.Error()) } diff --git a/cmd/signer/core/types.go b/cmd/signer/core/types.go index 03c3a58ff3..63dd9f3a61 100644 --- a/cmd/signer/core/types.go +++ b/cmd/signer/core/types.go @@ -50,6 +50,14 @@ func (a Account) String() string { } return err.Error() } +type ValidationInfo struct { + Typ string `json:"type"` + Message string `json:"message"` +} +type ValidationMessages struct { + Messages []ValidationInfo +} + /* // TransactionArg represents a Transaction for the signer. type TransactionArg struct { diff --git a/cmd/signer/core/validation.go b/cmd/signer/core/validation.go new file mode 100644 index 0000000000..64974e664b --- /dev/null +++ b/cmd/signer/core/validation.go @@ -0,0 +1,152 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "bytes" + "errors" + "fmt" + "github.com/ethereum/go-ethereum/common" + "math/big" +) + +// The validation package contains validation checks for transactions +// - ABI-data validation +// - Transaction semantics validation +// The package provides warnings for typical pitfalls + +func (vs *ValidationMessages) crit(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"CRITICAL", msg}) +} +func (vs *ValidationMessages) warn(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"WARNING", msg}) +} +func (vs *ValidationMessages) info(msg string) { + vs.Messages = append(vs.Messages, ValidationInfo{"Info", msg}) +} + +type Validator struct { + db *AbiDb +} + +func NewValidator(db *AbiDb) *Validator { + return &Validator{db} +} + +// validateCallData checks if the ABI-data + methodselector (if given) can be parsed and seems to match +func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, methodSelector *string) { + if len(data) == 0 { + return + } + if len(data) < 4 { + msgs.warn("Tx contains data which is not valid ABI") + return + } + var ( + selector string + err error + ) + // Try to make sense of the data + if methodSelector != nil { + selector = *methodSelector + } + + if selector == "" { + selector, err = v.db.LookupMethodSelector(data[:4]) + if err != nil { + msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err)) + return + } + } + if selector == "" { + // No more to do that this stage + return + } + abiData, err := MethodSelectorToAbi(selector) + if err != nil { + msgs.warn(fmt.Sprintf("Transaction data did not match ABI-interface: %v", err)) + return + } + + info, err := parseCallData(data, string(abiData)) + if err != nil { + msgs.warn(fmt.Sprintf("Transaction data did not match ABI-interface: %v", err)) + } else { + msgs.info(info.String()) + } + return +} + +// validateSemantics checks if the transactions 'makes sense', and generate warnings for a couple of typical scenarios +func (v *Validator) validate(msgs *ValidationMessages, txargs *SendTxArgs, methodSelector *string) error { + // Prevent accidental erroneous usage of both 'input' and 'data' + if txargs.Data != nil && txargs.Input != nil && !bytes.Equal(*txargs.Data, *txargs.Input) { + // This is a showstopper + return errors.New(`Ambiguous request: moth "data" and "input" are set and are not identical`) + } + var ( + data []byte + ) + // Place data on 'data', and nil 'input' + if txargs.Input != nil { + txargs.Data = txargs.Input + txargs.Input = nil + } + if txargs.Data != nil { + data = *txargs.Data + } + + if txargs.To == nil { + //Contract creation should contain sufficient data to deploy a contract + // A typical error is omitting sender due to some quirk in the javascript call + // e.g. https://github.com/ethereum/go-ethereum/issues/16106 + if len(data) == 0 { + if txargs.Value.ToInt().Cmp(big.NewInt(0)) > 0 { + // Sending ether into black hole + return errors.New(`Tx will create contract with value but empty code!`) + } + // No value submitted at least + msgs.crit("Tx will create contract with empty code!") + } else if len(data) < 40 { //Arbitrary limit + msgs.warn(fmt.Sprintf("Tx will will create contract, but payload is suspiciously small (%d b)", len(data))) + } + // methodSelector should be nil for contract creation + if methodSelector != nil { + msgs.warn("Tx will create contract, but method selector supplied; indicating intent to call a method.") + } + + } else { + if !txargs.To.ValidChecksum() { + msgs.warn("Invalid checksum on to-address") + } + // Normal transaction + if bytes.Equal(txargs.To.Address().Bytes(), common.Address{}.Bytes()) { + // Sending to 0 + msgs.crit("Tx destination is the zero address!") + } + // Validate calldata + v.validateCallData(msgs, data, methodSelector); + } + return nil +} + +// ValidateTransaction does a number of checks on the supplied transaction, and returns either a list of warnings, +// or an error, indicating that the transaction should be immediately rejected +func (v *Validator) ValidateTransaction(txArgs *SendTxArgs, methodSelector *string) (*ValidationMessages, error) { + msgs := &ValidationMessages{} + return msgs, v.validate(msgs, txArgs, methodSelector) +} diff --git a/cmd/signer/core/validation_test.go b/cmd/signer/core/validation_test.go new file mode 100644 index 0000000000..9b599d36c7 --- /dev/null +++ b/cmd/signer/core/validation_test.go @@ -0,0 +1,140 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package core + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "math/big" + "testing" +) + +func hexAddr(a string) common.Address { return common.BytesToAddress(common.FromHex(a)) } +func mixAddr(a string) (*common.MixedcaseAddress, error) { + return common.NewMixedcaseAddressFromString(a) +} +func toHexBig(h string) hexutil.Big { + b := big.NewInt(0).SetBytes(common.FromHex(h)) + return hexutil.Big(*b) +} +func toHexUint(h string) hexutil.Uint64 { + b := big.NewInt(0).SetBytes(common.FromHex(h)) + return hexutil.Uint64(b.Uint64()) +} +func dummyTxArgs(t txtestcase) *SendTxArgs { + to, _ := mixAddr(t.to) + from, _ := mixAddr(t.from) + n := toHexUint(t.n) + gas := toHexBig(t.g) + gasPrice := toHexBig(t.gp) + value := toHexBig(t.value) + var( + data, input *hexutil.Bytes + ) + if t.d != ""{ + a := hexutil.Bytes(common.FromHex(t.d)) + data = &a + } + if t.i != ""{ + a := hexutil.Bytes(common.FromHex(t.i)) + input = &a + + } + return &SendTxArgs{ + From: *from, + To: to, + Value: value, + Nonce: n, + GasPrice: gas, + Gas: gasPrice, + Data: data, + Input: input, + } +} + +type txtestcase struct { + from, to, n, g, gp, value, d, i string + expectErr bool + numMessages int +} + +func TestValidator(t *testing.T) { + var ( + // use empty db, there are other tests for the abi-specific stuff + db, _ = NewEmptyAbiDB() + v = NewValidator(db) + ) + testcases := []txtestcase{ + // Invalid to checksum + {from: "000000000000000000000000000000000000dead", to: "000000000000000000000000000000000000dead", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1}, + // valid 0x000000000000000000000000000000000000dEaD + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 0}, + // conflicting input and data + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", i: "0x02", expectErr: true, }, + // Data can't be parsed + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x0102", numMessages:1 }, + // Data (on Input) can't be parsed + {from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", i: "0x0102", numMessages:1 }, + // Send to 0 + {from: "000000000000000000000000000000000000dead", to: "0x0000000000000000000000000000000000000000", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1}, + // Create empty contract (no value) + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x00", numMessages: 1}, + // Create empty contract (with value) + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01", expectErr: true}, + // Small payload for create + {from: "000000000000000000000000000000000000dead", to: "", + n: "0x01", g: "0x20", gp: "0x40", value: "0x01",d:"0x01", numMessages: 1}, + + + } + for i, test := range testcases { + msgs, err := v.ValidateTransaction(dummyTxArgs(test), nil) + if err == nil && test.expectErr { + t.Errorf("Test %d, expected error", i) + for _, msg := range msgs.Messages { + fmt.Printf("* %s: %s\n", msg.Typ, msg.Message) + } + } + if err != nil && !test.expectErr { + t.Errorf("Test %d, unexpected error: %v", i, err) + } + if err == nil { + got := len(msgs.Messages) + if got != test.numMessages { + for _, msg := range msgs.Messages { + fmt.Printf("* %s: %s\n", msg.Typ, msg.Message) + } + t.Errorf("Test %d, expected %d messages, got %d", i,test.numMessages, got) + }else{ + //Debug printout, remove later + for _, msg := range msgs.Messages { + fmt.Printf("* [%d] %s: %s\n", i, msg.Typ, msg.Message) + } + fmt.Println() + } + } + } +} diff --git a/cmd/signer/rules/rules_test.go b/cmd/signer/rules/rules_test.go index a789923644..80024e9801 100644 --- a/cmd/signer/rules/rules_test.go +++ b/cmd/signer/rules/rules_test.go @@ -9,8 +9,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" "math/big" - "testing" "strings" + "testing" ) const JS = ` @@ -167,7 +167,7 @@ func TestSignTxRequest(t *testing.T) { Transaction: core.SendTxArgs{ From: *from, To: to}, - Callinfo: "", + Callinfo: nil, Meta: core.Metadata{"remoteip", "localip", "inproc"}, }) if err != nil { @@ -251,7 +251,7 @@ func TestForwarding(t *testing.T) { exp_calls := 8 if len(ui.calls) != exp_calls { - t.Errorf("Expected %d forwarded calls, got %d: %s", exp_calls, len(ui.calls), strings.Join(ui.calls,",")) + t.Errorf("Expected %d forwarded calls, got %d: %s", exp_calls, len(ui.calls), strings.Join(ui.calls, ",")) } @@ -425,8 +425,12 @@ func dummyTx(value hexutil.Big) *core.SignTxRequest { GasPrice: gas, Gas: gasPrice, }, - Callinfo: "Warning, all your base are bellong to us", - Meta: core.Metadata{"remoteip", "localip", "inproc"}, + Callinfo: &core.ValidationMessages{ + []core.ValidationInfo{ + {"Warning", "All your base are bellong to us"}, + }, + }, + Meta: core.Metadata{"remoteip", "localip", "inproc"}, } } func dummyTxWithV(value uint64) *core.SignTxRequest { @@ -484,7 +488,7 @@ func TestLimitWindow(t *testing.T) { } // dontCallMe is used as a next-handler that does not want to be called - it invokes test failure -type dontCallMe struct{ +type dontCallMe struct { t *testing.T } @@ -530,7 +534,6 @@ func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) { d.t.Fatalf("Did not expect next-handler to be called") } - //TestContextIsCleared tests that the rule-engine does not retain variables over several requests. // if it does, that would be bad since developers may rely on that to store data, // instead of using the disk-based data storage @@ -561,7 +564,7 @@ func TestContextIsCleared(t *testing.T) { tx := dummyTxWithV(0) r1, err := r.ApproveTx(tx) r2, err := r.ApproveTx(tx) - if r1.Approved != r2.Approved{ + if r1.Approved != r2.Approved { t.Errorf("Expected execution context to be cleared between executions") } }