mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
signer: implement custom 4byte databsae that saves submitted signatures
This commit is contained in:
parent
8bc021386f
commit
76a8c7caf0
18 changed files with 326 additions and 241 deletions
|
|
@ -58,14 +58,14 @@ Some snags and todos
|
||||||
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
|
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
|
||||||
a more secure mode by telling it that it only wants approve/deny capabilities.
|
a more secure mode by telling it that it only wants approve/deny capabilities.
|
||||||
|
|
||||||
* It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a
|
* [x] DONE: It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a
|
||||||
secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for
|
secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for
|
||||||
inclusion upstream.
|
inclusion upstream.
|
||||||
|
|
||||||
* It should be possible to configure the signer to check if an account is indeed known to it, before
|
* It should be possible to configure the signer to check if an account is indeed known to it, before
|
||||||
passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate
|
passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate
|
||||||
accounts if it immediately returned "unknown account". Similarly, it should be possible to configure
|
accounts if it immediately returned "unknown account".
|
||||||
the signer to auto-allow listing (certain) accounts, instead of asking every time.
|
* [x] DONE: Similarly, it should be possible to configure the signer to auto-allow listing (certain) accounts, instead of asking every time.
|
||||||
|
|
||||||
* Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode),
|
* Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode),
|
||||||
invoking methods with the following info:
|
invoking methods with the following info:
|
||||||
|
|
@ -131,6 +131,8 @@ process output for confirmation-requests.
|
||||||
|
|
||||||
## External API
|
## External API
|
||||||
|
|
||||||
|
See the [external api changelog](extapi_changelog.md) for information about changes to this API.
|
||||||
|
|
||||||
### Encoding
|
### Encoding
|
||||||
- number: positive integers that are hex encoded
|
- number: positive integers that are hex encoded
|
||||||
- data: hex encoded data
|
- data: hex encoded data
|
||||||
|
|
@ -497,7 +499,7 @@ See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to p
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
See the [ui api changelog](intapi_changelog.md) for information about changes to this API.
|
||||||
|
|
||||||
### ApproveTx
|
### ApproveTx
|
||||||
|
|
||||||
|
|
@ -777,6 +779,32 @@ 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`.
|
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
|
||||||
|
|
||||||
|
### OnSignerStartup
|
||||||
|
|
||||||
|
This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api,
|
||||||
|
in k/v-form.
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
```json
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "OnSignerStartup",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"extapi_http": "http://localhost:8550",
|
||||||
|
"extapi_ipc": null,
|
||||||
|
"extapi_version": "2.0.0",
|
||||||
|
"intapi_version": "1.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Rules for UI apis
|
### Rules for UI apis
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -159,11 +160,13 @@ func MethodSelectorToAbi(selector string) ([]byte, error) {
|
||||||
|
|
||||||
type AbiDb struct {
|
type AbiDb struct {
|
||||||
db map[string]string
|
db map[string]string
|
||||||
|
customdb map[string]string
|
||||||
|
customdbPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEmptyAbiDB exists for test purposes
|
// NewEmptyAbiDB exists for test purposes
|
||||||
func NewEmptyAbiDB() (*AbiDb, error) {
|
func NewEmptyAbiDB() (*AbiDb, error) {
|
||||||
return &AbiDb{make(map[string]string)}, nil
|
return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAbiDBFromFile loads signature database from file, and
|
// NewAbiDBFromFile loads signature database from file, and
|
||||||
|
|
@ -178,18 +181,74 @@ func NewAbiDBFromFile(path string) (*AbiDb, error) {
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewAbiDBFromFiles loads both the standard signature database and a custom database. The latter will be used
|
||||||
|
// to write new values into if they are submitted via the API
|
||||||
|
func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) {
|
||||||
|
|
||||||
|
db := &AbiDb{make(map[string]string), make(map[string]string), custom}
|
||||||
|
db.customdbPath = custom
|
||||||
|
|
||||||
|
raw, err := ioutil.ReadFile(standard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
json.Unmarshal(raw, &db.db)
|
||||||
|
// Custom file may not exist. Will be created during save, if needed
|
||||||
|
if _, err := os.Stat(custom); err == nil {
|
||||||
|
raw, err = ioutil.ReadFile(custom)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
json.Unmarshal(raw, &db.customdb)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
sig := common.ToHex(id)
|
sig := common.ToHex(id[:4])
|
||||||
if key, exists := db.db[sig]; exists {
|
if key, exists := db.db[sig]; exists {
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
if key, exists := db.customdb[sig]; exists {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// saveCustomAbi saves a signature ephemerally. If custom file is used, also saves to disk
|
||||||
|
func (db *AbiDb) saveCustomAbi(selector, signature string) error {
|
||||||
|
db.customdb[signature] = selector
|
||||||
|
if db.customdbPath == "" {
|
||||||
|
return nil //Not an error per se, just not used
|
||||||
|
}
|
||||||
|
d, err := json.Marshal(db.customdb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(db.customdbPath, d, 0600)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a signature to the database, if custom database saving is enabled.
|
||||||
|
// OBS: This method does _not_ validate the correctness of the data,
|
||||||
|
// it is assumed that the caller has already done so
|
||||||
|
func (db *AbiDb) AddSignature(selector string, data []byte) error {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := db.LookupMethodSelector(data[:4])
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sig := common.ToHex(data[:4])
|
||||||
|
return db.saveCustomAbi(selector, sig)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
// "reflect"
|
// "reflect"
|
||||||
// "math/big"
|
// "math/big"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
@ -210,3 +211,35 @@ func TestSelectorUnmarshalling(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomABI(t *testing.T) {
|
||||||
|
d, err := ioutil.TempDir("", "signer-4byte-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
filename := fmt.Sprintf("%s/4byte_custom.json", d)
|
||||||
|
abidb, err := NewAbiDBFromFiles("../4byte.json", filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Now we'll remove all existing signatures
|
||||||
|
abidb.db = make(map[string]string)
|
||||||
|
calldata := common.Hex2Bytes("a52c101edeadbeef")
|
||||||
|
_, err = abidb.LookupMethodSelector(calldata)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Should not find a match on empty db")
|
||||||
|
}
|
||||||
|
if err = abidb.AddSignature("send(uint256)", calldata); err != nil {
|
||||||
|
t.Fatalf("Failed to save file: %v", err)
|
||||||
|
}
|
||||||
|
_, err = abidb.LookupMethodSelector(calldata)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Should find a match for abi signature, got: %v", err)
|
||||||
|
}
|
||||||
|
//Check that it wrote to file
|
||||||
|
abidb2, err := NewAbiDBFromFile(filename)
|
||||||
|
_, err = abidb2.LookupMethodSelector(calldata)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Save failed: should find a match for abi signature after loading from disk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ type ExternalAPI interface {
|
||||||
|
|
||||||
// SignerUI specifies what method a UI needs to implement to be able to be used as a UI for the signer
|
// SignerUI specifies what method a UI needs to implement to be able to be used as a UI for the signer
|
||||||
type SignerUI interface {
|
type SignerUI interface {
|
||||||
|
|
||||||
// ApproveTx prompt the user for confirmation to request to sign Transaction
|
// ApproveTx prompt the user for confirmation to request to sign Transaction
|
||||||
ApproveTx(request *SignTxRequest) (SignTxResponse, error)
|
ApproveTx(request *SignTxRequest) (SignTxResponse, error)
|
||||||
// ApproveSignData prompt the user for confirmation to request to sign data
|
// ApproveSignData prompt the user for confirmation to request to sign data
|
||||||
|
|
@ -77,6 +76,9 @@ type SignerUI interface {
|
||||||
// OnApprovedTx notifies the UI about a transaction having been successfully signed.
|
// OnApprovedTx notifies the UI about a transaction having been successfully signed.
|
||||||
// This method can be used by a UI to keep track of e.g. how much has been sent to a particular recipient.
|
// This method can be used by a UI to keep track of e.g. how much has been sent to a particular recipient.
|
||||||
OnApprovedTx(tx ethapi.SignTransactionResult)
|
OnApprovedTx(tx ethapi.SignTransactionResult)
|
||||||
|
// OnSignerStartup is invoked when the signer boots, and tells the UI info about external API location and version
|
||||||
|
// information
|
||||||
|
OnSignerStartup(info StartupInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignerAPI defines the actual implementation of ExternalAPI
|
// SignerAPI defines the actual implementation of ExternalAPI
|
||||||
|
|
@ -180,6 +182,9 @@ type (
|
||||||
Message struct {
|
Message struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
|
StartupInfo struct {
|
||||||
|
Info map[string]interface{} `json:"info"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrRequestDenied = errors.New("Request denied")
|
var ErrRequestDenied = errors.New("Request denied")
|
||||||
|
|
@ -321,7 +326,7 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
|
||||||
err error
|
err error
|
||||||
result SignTxResponse
|
result SignTxResponse
|
||||||
)
|
)
|
||||||
msgs, err:= api.validator.ValidateTransaction(&args, methodSelector)
|
msgs, err := api.validator.ValidateTransaction(&args, methodSelector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import (
|
||||||
"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/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Used for testing
|
//Used for testing
|
||||||
|
|
@ -25,6 +25,9 @@ type HeadlessUI struct {
|
||||||
controller chan string
|
controller chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
fmt.Printf("OnApproved called")
|
fmt.Printf("OnApproved called")
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +247,7 @@ func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
|
||||||
nonce := (hexutil.Uint64)(0)
|
nonce := (hexutil.Uint64)(0)
|
||||||
data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
|
data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
|
||||||
tx := SendTxArgs{
|
tx := SendTxArgs{
|
||||||
From:from,
|
From: from,
|
||||||
To: &to,
|
To: &to,
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
GasPrice: gasPrice,
|
GasPrice: gasPrice,
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
}
|
}
|
||||||
fmt.Printf("from: %v\n", request.Transaction.From.String())
|
fmt.Printf("from: %v\n", request.Transaction.From.String())
|
||||||
fmt.Printf("value: %v wei\n", weival)
|
fmt.Printf("value: %v wei\n", weival)
|
||||||
if request.Transaction.Data != nil{
|
if request.Transaction.Data != nil {
|
||||||
d := *request.Transaction.Data
|
d := *request.Transaction.Data
|
||||||
if len(d) > 0 {
|
if len(d) > 0 {
|
||||||
fmt.Printf("data: %v\n", common.Bytes2Hex(d))
|
fmt.Printf("data: %v\n", common.Bytes2Hex(d))
|
||||||
|
|
@ -119,7 +119,7 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
}
|
}
|
||||||
if request.Callinfo != nil {
|
if request.Callinfo != nil {
|
||||||
fmt.Printf("\nTransaction validation:\n")
|
fmt.Printf("\nTransaction validation:\n")
|
||||||
for _,m := range request.Callinfo.Messages{
|
for _, m := range request.Callinfo.Messages {
|
||||||
fmt.Printf(" * %s : %s", m.Typ, m.Message)
|
fmt.Printf(" * %s : %s", m.Typ, m.Message)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
@ -235,3 +235,11 @@ func (ui *CommandlineUI) ShowInfo(message string) {
|
||||||
func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
fmt.Printf("Transaction signed: %v", tx.Tx.String())
|
fmt.Printf("Transaction signed: %v", tx.Tx.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) {
|
||||||
|
|
||||||
|
fmt.Printf("------- Signer info ------- ")
|
||||||
|
for k, v := range info.Info {
|
||||||
|
fmt.Printf("* %v : %v", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,11 @@ func NewStdIOUI() *StdIOUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch sends a request over the stdio
|
// dispatch sends a request over the stdio
|
||||||
func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error{
|
func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error {
|
||||||
var err error
|
var err error
|
||||||
if reply != nil{
|
if reply != nil {
|
||||||
err = ui.client.Call(nil, serviceMethod, args)
|
err = ui.client.Call(nil, serviceMethod, args)
|
||||||
}else{
|
} else {
|
||||||
err = ui.client.Call(&reply, serviceMethod, args)
|
err = ui.client.Call(&reply, serviceMethod, args)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -124,3 +124,10 @@ func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
|
log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *StdIOUI) OnSignerStartup(info StartupInfo) {
|
||||||
|
err := ui.dispatch("OnSignerStartup", info, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"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"
|
||||||
"math/big"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Accounts []Account
|
type Accounts []Account
|
||||||
|
|
@ -50,6 +50,7 @@ func (a Account) String() string {
|
||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidationInfo struct {
|
type ValidationInfo struct {
|
||||||
Typ string `json:"type"`
|
Typ string `json:"type"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,21 @@ type Validator struct {
|
||||||
func NewValidator(db *AbiDb) *Validator {
|
func NewValidator(db *AbiDb) *Validator {
|
||||||
return &Validator{db}
|
return &Validator{db}
|
||||||
}
|
}
|
||||||
|
func testSelector(selector string, data []byte) (*decodedCallData, error) {
|
||||||
|
if selector == "" {
|
||||||
|
return nil, fmt.Errorf("selector not found")
|
||||||
|
}
|
||||||
|
abiData, err := MethodSelectorToAbi(selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
info, err := parseCallData(data, string(abiData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// validateCallData checks if the ABI-data + methodselector (if given) can be parsed and seems to match
|
// 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) {
|
func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, methodSelector *string) {
|
||||||
|
|
@ -57,34 +72,30 @@ func (v *Validator) validateCallData(msgs *ValidationMessages, data []byte, meth
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
selector string
|
info *decodedCallData
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
// Try to make sense of the data
|
// Check the provided one
|
||||||
if methodSelector != nil {
|
if methodSelector != nil {
|
||||||
selector = *methodSelector
|
info, err = testSelector(*methodSelector, data)
|
||||||
|
if err != nil {
|
||||||
|
msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %v", err))
|
||||||
|
} else {
|
||||||
|
msgs.info(info.String())
|
||||||
|
//Successfull match. add to db if not there already (ignore errors there)
|
||||||
|
v.db.AddSignature(*methodSelector, data[:4])
|
||||||
}
|
}
|
||||||
|
return
|
||||||
if selector == "" {
|
}
|
||||||
selector, err = v.db.LookupMethodSelector(data[:4])
|
// Check the db
|
||||||
|
selector, err := v.db.LookupMethodSelector(data[:4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err))
|
msgs.warn(fmt.Sprintf("Tx contains data, but the ABI signature could not be found: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
info, err = testSelector(selector, data)
|
||||||
if selector == "" {
|
|
||||||
// No more to do that this stage
|
|
||||||
return
|
|
||||||
}
|
|
||||||
abiData, err := MethodSelectorToAbi(selector)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msgs.warn(fmt.Sprintf("Transaction data did not match ABI-interface: %v", err))
|
msgs.warn(fmt.Sprintf("Tx contains data, but provided ABI signature could not be matched: %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 {
|
} else {
|
||||||
msgs.info(info.String())
|
msgs.info(info.String())
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +150,7 @@ func (v *Validator) validate(msgs *ValidationMessages, txargs *SendTxArgs, metho
|
||||||
msgs.crit("Tx destination is the zero address!")
|
msgs.crit("Tx destination is the zero address!")
|
||||||
}
|
}
|
||||||
// Validate calldata
|
// Validate calldata
|
||||||
v.validateCallData(msgs, data, methodSelector);
|
v.validateCallData(msgs, data, methodSelector)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,14 @@ func dummyTxArgs(t txtestcase) *SendTxArgs {
|
||||||
gas := toHexBig(t.g)
|
gas := toHexBig(t.g)
|
||||||
gasPrice := toHexBig(t.gp)
|
gasPrice := toHexBig(t.gp)
|
||||||
value := toHexBig(t.value)
|
value := toHexBig(t.value)
|
||||||
var(
|
var (
|
||||||
data, input *hexutil.Bytes
|
data, input *hexutil.Bytes
|
||||||
)
|
)
|
||||||
if t.d != ""{
|
if t.d != "" {
|
||||||
a := hexutil.Bytes(common.FromHex(t.d))
|
a := hexutil.Bytes(common.FromHex(t.d))
|
||||||
data = &a
|
data = &a
|
||||||
}
|
}
|
||||||
if t.i != ""{
|
if t.i != "" {
|
||||||
a := hexutil.Bytes(common.FromHex(t.i))
|
a := hexutil.Bytes(common.FromHex(t.i))
|
||||||
input = &a
|
input = &a
|
||||||
|
|
||||||
|
|
@ -88,13 +88,13 @@ func TestValidator(t *testing.T) {
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 0},
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 0},
|
||||||
// conflicting input and data
|
// conflicting input and data
|
||||||
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", i: "0x02", expectErr: true, },
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", i: "0x02", expectErr: true},
|
||||||
// Data can't be parsed
|
// Data can't be parsed
|
||||||
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x0102", numMessages:1 },
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x0102", numMessages: 1},
|
||||||
// Data (on Input) can't be parsed
|
// Data (on Input) can't be parsed
|
||||||
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
{from: "000000000000000000000000000000000000dead", to: "0x000000000000000000000000000000000000dEaD",
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", i: "0x0102", numMessages:1 },
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", i: "0x0102", numMessages: 1},
|
||||||
// Send to 0
|
// Send to 0
|
||||||
{from: "000000000000000000000000000000000000dead", to: "0x0000000000000000000000000000000000000000",
|
{from: "000000000000000000000000000000000000dead", to: "0x0000000000000000000000000000000000000000",
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1},
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", numMessages: 1},
|
||||||
|
|
@ -106,9 +106,7 @@ func TestValidator(t *testing.T) {
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", expectErr: true},
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", expectErr: true},
|
||||||
// Small payload for create
|
// Small payload for create
|
||||||
{from: "000000000000000000000000000000000000dead", to: "",
|
{from: "000000000000000000000000000000000000dead", to: "",
|
||||||
n: "0x01", g: "0x20", gp: "0x40", value: "0x01",d:"0x01", numMessages: 1},
|
n: "0x01", g: "0x20", gp: "0x40", value: "0x01", d: "0x01", numMessages: 1},
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
for i, test := range testcases {
|
for i, test := range testcases {
|
||||||
msgs, err := v.ValidateTransaction(dummyTxArgs(test), nil)
|
msgs, err := v.ValidateTransaction(dummyTxArgs(test), nil)
|
||||||
|
|
@ -127,8 +125,8 @@ func TestValidator(t *testing.T) {
|
||||||
for _, msg := range msgs.Messages {
|
for _, msg := range msgs.Messages {
|
||||||
fmt.Printf("* %s: %s\n", msg.Typ, msg.Message)
|
fmt.Printf("* %s: %s\n", msg.Typ, msg.Message)
|
||||||
}
|
}
|
||||||
t.Errorf("Test %d, expected %d messages, got %d", i,test.numMessages, got)
|
t.Errorf("Test %d, expected %d messages, got %d", i, test.numMessages, got)
|
||||||
}else{
|
} else {
|
||||||
//Debug printout, remove later
|
//Debug printout, remove later
|
||||||
for _, msg := range msgs.Messages {
|
for _, msg := range msgs.Messages {
|
||||||
fmt.Printf("* [%d] %s: %s\n", i, msg.Typ, msg.Message)
|
fmt.Printf("* [%d] %s: %s\n", i, msg.Typ, msg.Message)
|
||||||
|
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
/* Demonstrates the following signer methods
|
|
||||||
|
|
||||||
- account_new: generate new password protected account
|
|
||||||
1. password: string
|
|
||||||
|
|
||||||
returns account object with address and URL
|
|
||||||
|
|
||||||
- account_list: listing of accounts
|
|
||||||
no args
|
|
||||||
|
|
||||||
returns array with accounts
|
|
||||||
|
|
||||||
- account_signTransaction: sign transaction and get tx in RLP encoded form back
|
|
||||||
1. from: address
|
|
||||||
2. passwd: string
|
|
||||||
3. transaction: object
|
|
||||||
|
|
||||||
returns signed transaction in RLP form (can be used with eth_sendRawTransaction)
|
|
||||||
|
|
||||||
- account_sign: calculate signature
|
|
||||||
1. from: address
|
|
||||||
2. passwd: string
|
|
||||||
3. data: hex string
|
|
||||||
|
|
||||||
returns signature
|
|
||||||
|
|
||||||
- account_ecRecover: derive address from signature
|
|
||||||
1. data: hex string
|
|
||||||
2. signature: hex string
|
|
||||||
|
|
||||||
returns address
|
|
||||||
*/
|
|
||||||
|
|
||||||
var spawn = require('child_process').spawn;
|
|
||||||
|
|
||||||
// by default the signer used the keystore for the mainnet, in this case it is pointed to a non-standard location.
|
|
||||||
// also it accepts the chainid, by default it uses the chainid for the mainnet.
|
|
||||||
const signer = spawn('./signer', ['-keystore', '/tmp/keystore', '-chainid', 5]);
|
|
||||||
const passwd = 'my password';
|
|
||||||
var createdAccountAddress = '0x';
|
|
||||||
var signData = '0xaabbccdd';
|
|
||||||
var signSignature = '0x';
|
|
||||||
var keystoreKeyData = '0x';
|
|
||||||
|
|
||||||
var currentRequest = -1;
|
|
||||||
function nextRequest() {
|
|
||||||
currentRequest++;
|
|
||||||
var req = null;
|
|
||||||
|
|
||||||
if (currentRequest < 7) {
|
|
||||||
req = {
|
|
||||||
id: currentRequest,
|
|
||||||
jsonrpc: "2.0"
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (currentRequest) {
|
|
||||||
case 0:
|
|
||||||
req.method = 'account_new';
|
|
||||||
req.params = [passwd];
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
req.method = 'account_list';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
req.method = 'account_signTransaction';
|
|
||||||
req.params = [createdAccountAddress, passwd, {
|
|
||||||
nonce: "0x0",
|
|
||||||
gasPrice: "0x1234",
|
|
||||||
gas: "0x55555",
|
|
||||||
value: "0x1234",
|
|
||||||
input: "0xabcd",
|
|
||||||
to: "0x07a565b7ed7d7a678680a4c162885bedbb695fe0"
|
|
||||||
}];
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
req.method = 'account_sign';
|
|
||||||
req.params = [createdAccountAddress, passwd, signData];
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
req.method = 'account_ecRecover';
|
|
||||||
req.params = [signData, signSignature];
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
req.method = 'account_export';
|
|
||||||
req.params = [createdAccountAddress]
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
req.method = 'account_import';
|
|
||||||
req.params = [keystoreKeyData, passwd, passwd];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
signer.stdout.on('data', (data) => {
|
|
||||||
console.log(`${data}`);
|
|
||||||
|
|
||||||
response = JSON.parse(`${data}`);
|
|
||||||
|
|
||||||
switch (response.id) {
|
|
||||||
case 0:
|
|
||||||
createdAccountAddress = response.result.address;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
signSignature = response.result;
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
if (createdAccountAddress !== response.result) {
|
|
||||||
console.error("expected address", createdAccountAddress, "got", response.result);
|
|
||||||
} else {
|
|
||||||
//console.log("Address recovered correct");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
keystoreKeyData = response.result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var req = nextRequest();
|
|
||||||
if (req !== null) {
|
|
||||||
req = JSON.stringify(req);
|
|
||||||
console.log(req);
|
|
||||||
signer.stdin.write(req);
|
|
||||||
} else {
|
|
||||||
signer.kill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
signer.stderr.on('data', (data) => {
|
|
||||||
console.log(`stderr: ${data}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
signer.on('close', (code) => {
|
|
||||||
//console.log(`signer process exited with code ${code}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// kickstart request cycle
|
|
||||||
req = JSON.stringify(nextRequest());
|
|
||||||
console.log(req);
|
|
||||||
signer.stdin.write(req);
|
|
||||||
25
cmd/signer/extapi_changelog.md
Normal file
25
cmd/signer/extapi_changelog.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
### Changelog for external API
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.0.0
|
||||||
|
|
||||||
|
* Commit `73abaf04b1372fa4c43201fb1b8019fe6b0a6f8d`, move `from` into `transaction` object in `signTransaction`. This
|
||||||
|
makes the `accounts_signTransaction` identical to the old `eth_signTransaction`.
|
||||||
|
|
||||||
|
|
||||||
|
#### 1.0.0
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
The API uses [semantic versioning](https://semver.org/).
|
||||||
|
|
||||||
|
TLDR; Given a version number MAJOR.MINOR.PATCH, increment the:
|
||||||
|
|
||||||
|
* MAJOR version when you make incompatible API changes,
|
||||||
|
* MINOR version when you add functionality in a backwards-compatible manner, and
|
||||||
|
* PATCH version when you make backwards-compatible bug fixes.
|
||||||
|
|
||||||
|
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
|
||||||
45
cmd/signer/intapi_changelog.md
Normal file
45
cmd/signer/intapi_changelog.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
### Changelog for internal API (ui-api)
|
||||||
|
|
||||||
|
#### 1.2.0
|
||||||
|
|
||||||
|
* Add `OnStartup` method, to provide the UI with information about what API version
|
||||||
|
the signer uses (both internal and external) aswell as build-info and external api.
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "OnSignerStartup",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"extapi_http": "http://localhost:8550",
|
||||||
|
"extapi_ipc": null,
|
||||||
|
"extapi_version": "2.0.0",
|
||||||
|
"intapi_version": "1.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.1.0
|
||||||
|
|
||||||
|
* Add `OnApproved` method
|
||||||
|
|
||||||
|
#### 1.0.0
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
The API uses [semantic versioning](https://semver.org/).
|
||||||
|
|
||||||
|
TLDR; Given a version number MAJOR.MINOR.PATCH, increment the:
|
||||||
|
|
||||||
|
* MAJOR version when you make incompatible API changes,
|
||||||
|
* MINOR version when you add functionality in a backwards-compatible manner, and
|
||||||
|
* PATCH version when you make backwards-compatible bug fixes.
|
||||||
|
|
||||||
|
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
|
||||||
|
|
@ -27,15 +27,21 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/signer/core"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EXT_API_VERSION -- see extapi_changelog.md
|
||||||
|
const EXT_API_VERSION = "2.0.0"
|
||||||
|
|
||||||
|
// INT_API_VERSION -- see intapi_changelog.md
|
||||||
|
const INT_API_VERSION = "1.2.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
|
|
@ -66,6 +72,11 @@ func main() {
|
||||||
Usage: "File containing 4byte-identifiers",
|
Usage: "File containing 4byte-identifiers",
|
||||||
Value: "./4byte.json",
|
Value: "./4byte.json",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "4bytedb-custom",
|
||||||
|
Usage: "File used for writing new 4byte-identifiers submitted via API",
|
||||||
|
Value: "./4byte-custom.json",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "auditlog",
|
Name: "auditlog",
|
||||||
Usage: "File used to emit audit logs. Set to \"\" to disable",
|
Usage: "File used to emit audit logs. Set to \"\" to disable",
|
||||||
|
|
@ -108,7 +119,7 @@ func main() {
|
||||||
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 := core.NewAbiDBFromFile(c.String("4bytedb"))
|
db, err := core.NewAbiDBFromFiles(c.String("4bytedb"), c.String("4bytedb-custom"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf(err.Error())
|
utils.Fatalf(err.Error())
|
||||||
|
|
@ -142,7 +153,6 @@ func main() {
|
||||||
if err = server.RegisterName("account", api); err != nil {
|
if err = server.RegisterName("account", api); err != nil {
|
||||||
utils.Fatalf("Could not register signer API: %v", err)
|
utils.Fatalf("Could not register signer API: %v", err)
|
||||||
}
|
}
|
||||||
//server.ListServices()
|
|
||||||
|
|
||||||
// Import from file
|
// Import from file
|
||||||
if rfile := c.String("requestfile"); rfile != "" {
|
if rfile := c.String("requestfile"); rfile != "" {
|
||||||
|
|
@ -155,13 +165,22 @@ func main() {
|
||||||
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||||
utils.Fatalf("Could not start http listener: %v", err)
|
utils.Fatalf("Could not start http listener: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint))
|
extapi_url := fmt.Sprintf("http://%s", endpoint)
|
||||||
|
log.Info("HTTP endpoint opened", "url", extapi_url)
|
||||||
cors := []string{"*"}
|
cors := []string{"*"}
|
||||||
|
|
||||||
if c.Bool("stdio-ui-test") {
|
if c.Bool("stdio-ui-test") {
|
||||||
log.Info("Performing UI test")
|
log.Info("Performing UI test")
|
||||||
go testExternalUI(api_impl)
|
go testExternalUI(api_impl)
|
||||||
}
|
}
|
||||||
|
ui.OnSignerStartup(core.StartupInfo{
|
||||||
|
Info: map[string]interface{}{
|
||||||
|
"extapi_version": EXT_API_VERSION,
|
||||||
|
"intapi_version": INT_API_VERSION,
|
||||||
|
"extapi_http": extapi_url,
|
||||||
|
"extapi_ipc": nil,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
rpc.NewHTTPServer(cors, server).Serve(listener)
|
rpc.NewHTTPServer(cors, server).Serve(listener)
|
||||||
|
|
||||||
|
|
@ -189,7 +208,7 @@ func testExternalUI(api *core.SignerAPI) {
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
_, err = api.SignTransaction(ctx, core.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)
|
||||||
|
|
|
||||||
|
|
@ -106,9 +106,9 @@ func (r *rulesetUi) execute(jsfunc string, jsarg interface{}) (otto.Value, error
|
||||||
}
|
}
|
||||||
// Now, we call foobar(JSON.parse(<jsondata>)).
|
// Now, we call foobar(JSON.parse(<jsondata>)).
|
||||||
var call string
|
var call string
|
||||||
if(len(jsonbytes) > 0){
|
if len(jsonbytes) > 0 {
|
||||||
call = fmt.Sprintf("%v(JSON.parse(%v))", jsfunc, string(jsonbytes))
|
call = fmt.Sprintf("%v(JSON.parse(%v))", jsfunc, string(jsonbytes))
|
||||||
}else{
|
} else {
|
||||||
call = fmt.Sprintf("%v()", jsfunc)
|
call = fmt.Sprintf("%v()", jsfunc)
|
||||||
}
|
}
|
||||||
return vm.Run(call)
|
return vm.Run(call)
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ func mixAddr(a string) (*common.MixedcaseAddress, error) {
|
||||||
|
|
||||||
type alwaysDenyUi struct{}
|
type alwaysDenyUi struct{}
|
||||||
|
|
||||||
|
func (alwaysDenyUi) OnSignerStartup(info core.StartupInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
func (alwaysDenyUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
func (alwaysDenyUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
||||||
return core.SignTxResponse{request.Transaction, false, ""}, nil
|
return core.SignTxResponse{request.Transaction, false, ""}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -223,6 +226,8 @@ func (d *dummyUi) ShowInfo(message string) {
|
||||||
func (d *dummyUi) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
func (d *dummyUi) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
d.calls = append(d.calls, "OnApprovedTx")
|
d.calls = append(d.calls, "OnApprovedTx")
|
||||||
}
|
}
|
||||||
|
func (d *dummyUi) OnSignerStartup(info core.StartupInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
//TestForwarding tests that the rule-engine correctly dispatches requests to the next caller
|
//TestForwarding tests that the rule-engine correctly dispatches requests to the next caller
|
||||||
func TestForwarding(t *testing.T) {
|
func TestForwarding(t *testing.T) {
|
||||||
|
|
@ -492,6 +497,9 @@ type dontCallMe struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
||||||
d.t.Fatalf("Did not expect next-handler to be called")
|
d.t.Fatalf("Did not expect next-handler to be called")
|
||||||
return core.SignTxResponse{}, core.ErrRequestDenied
|
return core.SignTxResponse{}, core.ErrRequestDenied
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func (s *EphemeralStorage) Get(key string) string {
|
||||||
func (s *EphemeralStorage) New(namespace string) Storage {
|
func (s *EphemeralStorage) New(namespace string) Storage {
|
||||||
child := &EphemeralStorage{
|
child := &EphemeralStorage{
|
||||||
data: make(map[string]string),
|
data: make(map[string]string),
|
||||||
namespace: fmt.Sprintf("%s.%s", namespace),
|
namespace: fmt.Sprintf("%s.%s", s.namespace, namespace),
|
||||||
}
|
}
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
const net = require('net');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
let conn = net.connect(path.join('\\\\?\\pipe', 'ethereum-signer'))
|
|
||||||
|
|
||||||
const req = {
|
|
||||||
id: 1234,
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
//method: 'account_list',
|
|
||||||
method: 'account_signTransaction',
|
|
||||||
params: ['0xaabbccddaabbccddaabbccddaabbccddaabbccdd', {
|
|
||||||
to: '0x0011223344556677889900112233445566778899',
|
|
||||||
value: '0x123450000',
|
|
||||||
data: '0xabcdef',
|
|
||||||
gas: '0x12345',
|
|
||||||
gasPrice: '0x67890'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.on('data', (data) => {
|
|
||||||
console.log(data.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
conn.write(JSON.stringify(req));
|
|
||||||
Loading…
Reference in a new issue