mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
signer: implement OnApprovedTx, change signing response (API BREAKAGE)
This commit is contained in:
parent
da312a1dcb
commit
a8e68b9cc3
9 changed files with 280 additions and 131 deletions
|
|
@ -33,8 +33,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
||||||
"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/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -46,7 +46,7 @@ type ExternalAPI interface {
|
||||||
// New request to create a new account
|
// New request to create a new account
|
||||||
New(ctx context.Context) (accounts.Account, error)
|
New(ctx context.Context) (accounts.Account, error)
|
||||||
// SignTransaction request to sign the specified transaction
|
// SignTransaction request to sign the specified transaction
|
||||||
SignTransaction(ctx context.Context, from common.MixedcaseAddress, args TransactionArg, methodSelector *string) (hexutil.Bytes, error)
|
SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error)
|
||||||
// Sign - request to sign the given data (plus prefix)
|
// Sign - request to sign the given data (plus prefix)
|
||||||
Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error)
|
Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error)
|
||||||
// EcRecover - request to perform ecrecover
|
// EcRecover - request to perform ecrecover
|
||||||
|
|
@ -77,6 +77,9 @@ type SignerUI interface {
|
||||||
ShowError(message string)
|
ShowError(message string)
|
||||||
// ShowInfo displays info message to user
|
// ShowInfo displays info message to user
|
||||||
ShowInfo(message string)
|
ShowInfo(message string)
|
||||||
|
// 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.
|
||||||
|
OnApprovedTx(tx ethapi.SignTransactionResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignerAPI defines the actual implementation of ExternalAPI
|
// SignerAPI defines the actual implementation of ExternalAPI
|
||||||
|
|
@ -123,16 +126,14 @@ func (m Metadata) String() string {
|
||||||
type (
|
type (
|
||||||
// SignTxRequest contains info about a Transaction to sign
|
// SignTxRequest contains info about a Transaction to sign
|
||||||
SignTxRequest struct {
|
SignTxRequest struct {
|
||||||
Transaction TransactionArg `json:"transaction"`
|
Transaction SendTxArgs `json:"transaction"`
|
||||||
From common.MixedcaseAddress `json:"from"`
|
|
||||||
Callinfo string `json:"call_info"`
|
Callinfo string `json:"call_info"`
|
||||||
Meta Metadata `json:"meta"`
|
Meta Metadata `json:"meta"`
|
||||||
}
|
}
|
||||||
// SignTxResponse result from SignTxRequest
|
// SignTxResponse result from SignTxRequest
|
||||||
SignTxResponse struct {
|
SignTxResponse struct {
|
||||||
//The UI may make changes to the TX
|
//The UI may make changes to the TX
|
||||||
Transaction TransactionArg `json:"transaction"`
|
Transaction SendTxArgs `json:"transaction"`
|
||||||
From common.MixedcaseAddress `json:"from"`
|
|
||||||
Approved bool `json:"approved"`
|
Approved bool `json:"approved"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
@ -271,23 +272,17 @@ func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) {
|
||||||
return be[0].(*keystore.KeyStore).NewAccount(resp.Password)
|
return be[0].(*keystore.KeyStore).NewAccount(resp.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toTransaction(args *TransactionArg) *types.Transaction {
|
|
||||||
if args.To == nil {
|
|
||||||
return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
|
|
||||||
} else {
|
|
||||||
return types.NewTransaction(uint64(*args.Nonce), args.To.Address(), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logDiff logs the difference between the incoming (original) transaction and the one returned from the signer.
|
// logDiff logs the difference between the incoming (original) transaction and the one returned from the signer.
|
||||||
// it also returns 'true' if the transaction was modified, to make it possible to configure the signer not to allow
|
// it also returns 'true' if the transaction was modified, to make it possible to configure the signer not to allow
|
||||||
// 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.From, new.From; f0 != f1 {
|
|
||||||
modified = true
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -310,9 +305,19 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
|
||||||
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; !bytes.Equal(d0, d1) {
|
if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 {
|
||||||
|
d0s := ""
|
||||||
|
d1s := ""
|
||||||
|
if d0 != nil {
|
||||||
|
d0s = common.ToHex(*d0)
|
||||||
|
}
|
||||||
|
if d1 != nil {
|
||||||
|
d1s = common.ToHex(*d1)
|
||||||
|
}
|
||||||
|
if d1s != d0s {
|
||||||
modified = true
|
modified = true
|
||||||
log.Info("Data changed by UI", "was", common.ToHex(d0), "is", common.ToHex(d1))
|
log.Info("Data changed by UI", "was", d0s, "is", d1s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 {
|
if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 {
|
||||||
|
|
||||||
|
|
@ -324,41 +329,68 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
|
||||||
return modified
|
return modified
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTransaction signs the given Transaction and returns it in an RLP encoded form
|
// determineCallInfo turns ABI-data + methodselector (if given) into a string suitable
|
||||||
// that can be posted to `eth_sendRawTransaction`.
|
// to present to the user.
|
||||||
func (api *SignerAPI) SignTransaction(ctx context.Context, from common.MixedcaseAddress, args TransactionArg, methodSelector *string) (hexutil.Bytes, error) {
|
func (api *SignerAPI) determineCallInfo(data []byte, methodSelector *string) string {
|
||||||
|
|
||||||
|
if len(data) < 4 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
|
selector string
|
||||||
err error
|
err error
|
||||||
result SignTxResponse
|
|
||||||
)
|
)
|
||||||
req := SignTxRequest{Transaction: args, From: from, Meta: MetadataFromContext(ctx)}
|
|
||||||
data := args.Data
|
|
||||||
if len(data) > 3 {
|
|
||||||
// Try to make sense of the data
|
// Try to make sense of the data
|
||||||
var selector string
|
|
||||||
if methodSelector == nil {
|
if methodSelector == nil {
|
||||||
selector, err = api.abidb.LookupMethodSelector(data[:4])
|
selector, err = api.abidb.LookupMethodSelector(data[:4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.Callinfo = errorWrapper{"Warning! Could not locate ABI", err}.String()
|
return errorWrapper{"Warning! Could not locate ABI", err}.String()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selector = *methodSelector
|
selector = *methodSelector
|
||||||
}
|
}
|
||||||
if selector != "" {
|
if selector != "" {
|
||||||
abidata, err := MethodSelectorToAbi(selector)
|
abiData, err := MethodSelectorToAbi(selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.Callinfo = errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String()
|
return errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String()
|
||||||
} else {
|
} else {
|
||||||
var info *decodedCallData
|
var info *decodedCallData
|
||||||
info, err = parseCallData(data, string(abidata))
|
info, err = parseCallData(data, string(abiData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.Callinfo = errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String()
|
return errorWrapper{"Warning! Could not validate ABI-data against calldata", err}.String()
|
||||||
} else {
|
} else {
|
||||||
req.Callinfo = info.String()
|
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`)
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
|
@ -368,25 +400,33 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, from common.Mixedcase
|
||||||
}
|
}
|
||||||
// Log changes made by the UI to the signing-request
|
// Log changes made by the UI to the signing-request
|
||||||
logDiff(&req, &result)
|
logDiff(&req, &result)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
acc accounts.Account
|
acc accounts.Account
|
||||||
wallet accounts.Wallet
|
wallet accounts.Wallet
|
||||||
)
|
)
|
||||||
acc = accounts.Account{Address: result.From.Address()}
|
acc = accounts.Account{Address: result.Transaction.From.Address()}
|
||||||
wallet, err = api.am.Find(acc)
|
wallet, err = api.am.Find(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var tx = toTransaction(&result.Transaction)
|
// Convert fields into a real transaction
|
||||||
|
var unsignedTx = result.Transaction.toTransaction()
|
||||||
|
|
||||||
// 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, tx, 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
|
||||||
}
|
}
|
||||||
return rlp.EncodeToBytes(signedTx)
|
|
||||||
|
rlpdata, err := rlp.EncodeToBytes(signedTx)
|
||||||
|
response := ethapi.SignTransactionResult{rlpdata, signedTx}
|
||||||
|
|
||||||
|
// Finally, send the signed tx to the UI
|
||||||
|
api.ui.OnApprovedTx(response)
|
||||||
|
// ...and to the external caller
|
||||||
|
return &response, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates an Ethereum ECDSA signature for:
|
// Sign calculates an Ethereum ECDSA signature for:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"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/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Used for testing
|
//Used for testing
|
||||||
|
|
@ -24,18 +25,22 @@ type HeadlessUI struct {
|
||||||
controller chan string
|
controller chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
|
fmt.Printf("OnApproved called")
|
||||||
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
||||||
|
|
||||||
switch <-ui.controller {
|
switch <-ui.controller {
|
||||||
case "Y":
|
case "Y":
|
||||||
return SignTxResponse{request.Transaction, request.From, 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, request.From, true, <-ui.controller}, nil
|
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
|
||||||
default:
|
default:
|
||||||
return SignTxResponse{request.Transaction, request.From, false, ""}, nil
|
return SignTxResponse{request.Transaction, false, ""}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
||||||
|
|
@ -231,19 +236,21 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
|
t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func mkTestTx() TransactionArg {
|
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)
|
||||||
tx := TransactionArg{
|
data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a"))
|
||||||
&to,
|
tx := SendTxArgs{
|
||||||
gas,
|
From:from,
|
||||||
gasPrice,
|
To: &to,
|
||||||
value,
|
Gas: gas,
|
||||||
common.Hex2Bytes("01020304050607080a"),
|
GasPrice: gasPrice,
|
||||||
&nonce}
|
Value: value,
|
||||||
|
Data: &data,
|
||||||
|
Nonce: &nonce}
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,8 +258,7 @@ func TestSignTx(t *testing.T) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
list Accounts
|
list Accounts
|
||||||
h []byte
|
res, res2 *ethapi.SignTransactionResult
|
||||||
h2 []byte
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -266,22 +272,22 @@ func TestSignTx(t *testing.T) {
|
||||||
a := common.NewMixedcaseAddress(list[0].Address)
|
a := common.NewMixedcaseAddress(list[0].Address)
|
||||||
|
|
||||||
methodSig := "test(uint)"
|
methodSig := "test(uint)"
|
||||||
tx := mkTestTx()
|
tx := mkTestTx(a)
|
||||||
|
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
control <- "wrongpassword"
|
control <- "wrongpassword"
|
||||||
h, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if h != nil {
|
if res != nil {
|
||||||
t.Errorf("Expected nil-data, got %h", h)
|
t.Errorf("Expected nil-response, got %v", res)
|
||||||
}
|
}
|
||||||
if err != keystore.ErrDecrypt {
|
if err != keystore.ErrDecrypt {
|
||||||
t.Errorf("Expected ErrLocked! %v", err)
|
t.Errorf("Expected ErrLocked! %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
control <- "No way"
|
control <- "No way"
|
||||||
h, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if h != nil {
|
if res != nil {
|
||||||
t.Errorf("Expected nil-data, got %h", h)
|
t.Errorf("Expected nil-response, got %v", res)
|
||||||
}
|
}
|
||||||
if err != ErrRequestDenied {
|
if err != ErrRequestDenied {
|
||||||
t.Errorf("Expected ErrRequestDenied! %v", err)
|
t.Errorf("Expected ErrRequestDenied! %v", err)
|
||||||
|
|
@ -289,13 +295,13 @@ func TestSignTx(t *testing.T) {
|
||||||
|
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
control <- "apassword"
|
control <- "apassword"
|
||||||
h, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
parsedTx := &types.Transaction{}
|
parsedTx := &types.Transaction{}
|
||||||
rlp.Decode(bytes.NewReader(h), parsedTx)
|
rlp.Decode(bytes.NewReader(res.Raw), parsedTx)
|
||||||
//The tx should NOT be modified by the UI
|
//The tx should NOT be modified by the UI
|
||||||
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
|
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
|
||||||
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
|
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
|
||||||
|
|
@ -303,11 +309,11 @@ func TestSignTx(t *testing.T) {
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
control <- "apassword"
|
control <- "apassword"
|
||||||
|
|
||||||
h2, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
|
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(h, h2) {
|
if !bytes.Equal(res.Raw, res2.Raw) {
|
||||||
t.Error("Expected tx to be unmodified by UI")
|
t.Error("Expected tx to be unmodified by UI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,19 +321,19 @@ func TestSignTx(t *testing.T) {
|
||||||
control <- "M"
|
control <- "M"
|
||||||
control <- "apassword"
|
control <- "apassword"
|
||||||
|
|
||||||
h2, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
|
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedTx2 := &types.Transaction{}
|
parsedTx2 := &types.Transaction{}
|
||||||
rlp.Decode(bytes.NewReader(h), parsedTx2)
|
rlp.Decode(bytes.NewReader(res.Raw), parsedTx2)
|
||||||
//The tx should NOT be modified by the UI
|
//The tx should be modified by the UI
|
||||||
if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 {
|
if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 {
|
||||||
t.Errorf("Expected value to be changed, got %v", parsedTx.Value())
|
t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(h, h2) {
|
if bytes.Equal(res.Raw, res2.Raw) {
|
||||||
t.Error("Expected tx to be modified by UI")
|
t.Error("Expected tx to be modified by UI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ 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/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuditLogger struct {
|
type AuditLogger struct {
|
||||||
|
|
@ -41,15 +42,17 @@ func (l *AuditLogger) New(ctx context.Context) (accounts.Account, error) {
|
||||||
return l.api.New(ctx)
|
return l.api.New(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *AuditLogger) SignTransaction(ctx context.Context, from common.MixedcaseAddress, args TransactionArg, methodSelector *string) (hexutil.Bytes, error) {
|
func (l *AuditLogger) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
|
||||||
l.log.Info("SignTransaction", "type", "request", "metadata", MetadataFromContext(ctx).String(),
|
l.log.Info("SignTransaction", "type", "request", "metadata", MetadataFromContext(ctx).String(),
|
||||||
"from", from.String(), "tx", args.String(),
|
"tx", args.String(),
|
||||||
"methodSelector", methodSelector)
|
"methodSelector", methodSelector)
|
||||||
b, e := l.api.SignTransaction(ctx, from, args, methodSelector)
|
res, e := l.api.SignTransaction(ctx, args, methodSelector)
|
||||||
|
if res != nil{
|
||||||
l.log.Info("SignTransaction", "type", "response", "data", common.Bytes2Hex(b), "error", e)
|
l.log.Info("SignTransaction", "type", "response", "data", common.Bytes2Hex(res.Raw), "error", e)
|
||||||
|
}else{
|
||||||
return b, e
|
l.log.Info("SignTransaction", "type", "response", "data", res, "error", e)
|
||||||
|
}
|
||||||
|
return res, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *AuditLogger) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
|
func (l *AuditLogger) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
@ -108,10 +109,13 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("to: <contact creation>\n")
|
fmt.Printf("to: <contact creation>\n")
|
||||||
}
|
}
|
||||||
fmt.Printf("from: %v\n", request.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 len(request.Transaction.Data) > 0 {
|
if request.Transaction.Data != nil{
|
||||||
fmt.Printf("data: %v\n", common.Bytes2Hex(request.Transaction.Data))
|
d := *request.Transaction.Data
|
||||||
|
if len(d) > 0 {
|
||||||
|
fmt.Printf("data: %v\n", common.Bytes2Hex(d))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if request.Callinfo != "" {
|
if request.Callinfo != "" {
|
||||||
fmt.Printf("\nNote: This Transaction contains data. Review abi-decoding info below:")
|
fmt.Printf("\nNote: This Transaction contains data. Review abi-decoding info below:")
|
||||||
|
|
@ -122,9 +126,9 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
showMetadata(request.Meta)
|
showMetadata(request.Meta)
|
||||||
fmt.Printf("-------------------------------------------\n")
|
fmt.Printf("-------------------------------------------\n")
|
||||||
if !ui.confirm() {
|
if !ui.confirm() {
|
||||||
return SignTxResponse{request.Transaction, request.From, false, ""}, nil
|
return SignTxResponse{request.Transaction, false, ""}, nil
|
||||||
}
|
}
|
||||||
return SignTxResponse{request.Transaction, request.From, true, ui.readPassword()}, nil
|
return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApproveSignData prompt the user for confirmation to request to sign data
|
// ApproveSignData prompt the user for confirmation to request to sign data
|
||||||
|
|
@ -222,6 +226,9 @@ func (ui *CommandlineUI) ShowError(message string) {
|
||||||
|
|
||||||
// ShowInfo displays info message to user
|
// ShowInfo displays info message to user
|
||||||
func (ui *CommandlineUI) ShowInfo(message string) {
|
func (ui *CommandlineUI) ShowInfo(message string) {
|
||||||
|
|
||||||
fmt.Printf("Info: %v\n", message)
|
fmt.Printf("Info: %v\n", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
|
fmt.Printf("Transaction signed: %v", tx.Tx.String())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ func testExternalUI(api *SignerAPI) {
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
_, err = api.SignTransaction(ctx, common.MixedcaseAddress{}, TransactionArg{}, nil)
|
_, err = api.SignTransaction(ctx, 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)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/signer"
|
"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/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -84,7 +85,7 @@ func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v, err := r.vm.Call("ApproveTx", nil, string(jsarg))
|
v, err := r.vm.Call(jsfunc, nil, string(jsarg))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("error occurred during execution", "error", err)
|
log.Info("error occurred during execution", "error", err)
|
||||||
|
|
@ -106,7 +107,7 @@ func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) error
|
||||||
func (r *rulesetUi) ApproveTx(request *signer.SignTxRequest) (signer.SignTxResponse, error) {
|
func (r *rulesetUi) ApproveTx(request *signer.SignTxRequest) (signer.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, From: request.From, Approved: true, Password: ""}, nil
|
return signer.SignTxResponse{Transaction: request.Transaction, Approved: true, Password: ""}, nil
|
||||||
}
|
}
|
||||||
return signer.SignTxResponse{Approved: false}, err
|
return signer.SignTxResponse{Approved: false}, err
|
||||||
}
|
}
|
||||||
|
|
@ -156,3 +157,17 @@ func (r *rulesetUi) ShowInfo(message string) {
|
||||||
log.Info(message)
|
log.Info(message)
|
||||||
r.next.ShowInfo(message)
|
r.next.ShowInfo(message)
|
||||||
}
|
}
|
||||||
|
func (r *rulesetUi) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
|
|
||||||
|
jsonTx, err := json.Marshal(tx)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed marshalling transaction", "tx", tx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = r.vm.Call("OnApprovedTx", nil, string(jsonTx))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error in onapprove %v", err)
|
||||||
|
log.Warn("error occurred during execution", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/signer"
|
"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/internal/ethapi"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -75,7 +77,7 @@ func TestListRequest(t *testing.T) {
|
||||||
accs[i] = acc
|
accs[i] = acc
|
||||||
}
|
}
|
||||||
|
|
||||||
js := `function ApproveListing(accounts, meta){ return "Approve" }`
|
js := `function ApproveListing(){ return "Approve" }`
|
||||||
|
|
||||||
r, err := initRuleEngine(js)
|
r, err := initRuleEngine(js)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -99,12 +101,12 @@ func TestSignTxRequest(t *testing.T) {
|
||||||
function ApproveTx(jsonstr){
|
function ApproveTx(jsonstr){
|
||||||
console.log(jsonstr)
|
console.log(jsonstr)
|
||||||
r = JSON.parse(jsonstr)
|
r = JSON.parse(jsonstr)
|
||||||
console.log("from", r.from)
|
console.log("transaction.from", r.transaction.from);
|
||||||
console.log("transaction.to", r.transaction.to);
|
console.log("transaction.to", r.transaction.to);
|
||||||
console.log("transaction.value", r.transaction.value);
|
console.log("transaction.value", r.transaction.value);
|
||||||
console.log("transaction.nonce", r.transaction.nonce);
|
console.log("transaction.nonce", r.transaction.nonce);
|
||||||
if(r.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
|
if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
|
||||||
if(r.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
|
if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
r, err := initRuleEngine(js)
|
r, err := initRuleEngine(js)
|
||||||
|
|
@ -125,8 +127,9 @@ func TestSignTxRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
fmt.Printf("to %v", to.Address().String())
|
fmt.Printf("to %v", to.Address().String())
|
||||||
resp, err := r.ApproveTx(&signer.SignTxRequest{
|
resp, err := r.ApproveTx(&signer.SignTxRequest{
|
||||||
Transaction: signer.TransactionArg{To: to},
|
Transaction: signer.SendTxArgs{
|
||||||
From: *from,
|
From: *from,
|
||||||
|
To: to},
|
||||||
Callinfo: "",
|
Callinfo: "",
|
||||||
Meta: signer.Metadata{"remoteip", "localip", "inproc"},
|
Meta: signer.Metadata{"remoteip", "localip", "inproc"},
|
||||||
})
|
})
|
||||||
|
|
@ -243,46 +246,79 @@ const ExampleTxWindow = `
|
||||||
sum = new BigNumber(0)
|
sum = new BigNumber(0)
|
||||||
|
|
||||||
sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
|
sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
|
||||||
console.log("Sum so far", sum);
|
console.log("ApproveTx > Sum so far", sum);
|
||||||
console.log("Requested", value.toNumber());
|
console.log("ApproveTx > Requested", value.toNumber());
|
||||||
|
|
||||||
// Would we exceed weekly limit ?
|
// Would we exceed weekly limit ?
|
||||||
if (sum.plus(value).lt(limit)){
|
return sum.plus(value).lt(limit)
|
||||||
// Add this to the storage
|
|
||||||
newtxs.push({tstamp: new Date().getTime(), value: value});
|
|
||||||
storage.Put("txs", JSON.stringify(newtxs));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
function ApproveTx(jsonstr){
|
function ApproveTx(jsonstr){
|
||||||
r = JSON.parse(jsonstr);
|
var r = JSON.parse(jsonstr)
|
||||||
console.log("Requested value ", r.transaction.value)
|
|
||||||
|
|
||||||
if (isLimitOk(r.transaction)){
|
if (isLimitOk(r.transaction)){
|
||||||
return "Approve"
|
return "Approve"
|
||||||
}
|
}
|
||||||
return "Nope"
|
return "Nope"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter
|
||||||
|
* 'response_str' contains the return value that will be sent to the external caller.
|
||||||
|
* The return value from this method is ignore - 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.
|
||||||
|
* If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user
|
||||||
|
* then accepts the transaction, this method will be called.
|
||||||
|
*
|
||||||
|
* TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
|
||||||
|
*/
|
||||||
|
function OnApprovedTx(response_str){
|
||||||
|
console.log("OnApprovedTx > called with data\n\t "+response_str)
|
||||||
|
var resp = JSON.parse(response_str)
|
||||||
|
var value = big(resp.tx.value)
|
||||||
|
var txs = []
|
||||||
|
// Load stored transactions
|
||||||
|
var stored = storage.Get('txs');
|
||||||
|
if(stored != ""){
|
||||||
|
txs = JSON.parse(stored)
|
||||||
|
}
|
||||||
|
// Add this to the storage
|
||||||
|
txs.push({tstamp: new Date().getTime(), value: value});
|
||||||
|
storage.Put("txs", JSON.stringify(txs));
|
||||||
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func dummyTx(value *hexutil.Big) *signer.SignTxRequest {
|
func dummyTx(value *hexutil.Big) *signer.SignTxRequest {
|
||||||
|
|
||||||
to, _ := mixAddr("000000000000000000000000000000000000dead")
|
to, _ := mixAddr("000000000000000000000000000000000000dead")
|
||||||
from, _ := mixAddr("000000000000000000000000000000000000dead")
|
from, _ := mixAddr("000000000000000000000000000000000000dead")
|
||||||
|
n := hexutil.Uint64(3)
|
||||||
|
gas := hexutil.Big(*big.NewInt(21000))
|
||||||
|
gasPrice := hexutil.Big(*big.NewInt(2000000))
|
||||||
|
|
||||||
return &signer.SignTxRequest{
|
return &signer.SignTxRequest{
|
||||||
Transaction: signer.TransactionArg{
|
Transaction: signer.SendTxArgs{
|
||||||
|
From: *from,
|
||||||
To: to,
|
To: to,
|
||||||
Value: value,
|
Value: value,
|
||||||
|
Nonce: &n,
|
||||||
|
GasPrice: &gas,
|
||||||
|
Gas: &gasPrice,
|
||||||
},
|
},
|
||||||
From: *from,
|
|
||||||
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: signer.Metadata{"remoteip", "localip", "inproc"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func dummySigned(value *big.Int) *types.Transaction {
|
||||||
|
to := common.HexToAddress("000000000000000000000000000000000000dead")
|
||||||
|
gas := big.NewInt(21000)
|
||||||
|
gasPrice := big.NewInt(2000000)
|
||||||
|
data := make([]byte, 0)
|
||||||
|
return types.NewTransaction(3, to, value, gas, gasPrice, data)
|
||||||
|
|
||||||
|
}
|
||||||
func TestLimitWindow(t *testing.T) {
|
func TestLimitWindow(t *testing.T) {
|
||||||
|
|
||||||
r, err := initRuleEngine(ExampleTxWindow)
|
r, err := initRuleEngine(ExampleTxWindow)
|
||||||
|
|
@ -299,13 +335,21 @@ 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++ {
|
||||||
resp, err := r.ApproveTx(dummyTx(&h))
|
unsigned := dummyTx(&h)
|
||||||
|
resp, err := r.ApproveTx(unsigned)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %v", err)
|
t.Errorf("Unexpected error %v", err)
|
||||||
}
|
}
|
||||||
if !resp.Approved {
|
if !resp.Approved {
|
||||||
t.Errorf("Expected check to resolve to 'Approve'")
|
t.Errorf("Expected check to resolve to 'Approve'")
|
||||||
}
|
}
|
||||||
|
// Create a dummy signed transaction
|
||||||
|
|
||||||
|
response := ethapi.SignTransactionResult{
|
||||||
|
Tx: dummySigned(v),
|
||||||
|
Raw: common.Hex2Bytes("deadbeef"),
|
||||||
|
}
|
||||||
|
r.OnApprovedTx(response)
|
||||||
}
|
}
|
||||||
// Fourth should fail
|
// Fourth should fail
|
||||||
resp, err := r.ApproveTx(dummyTx(&h))
|
resp, err := r.ApproveTx(dummyTx(&h))
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,12 @@
|
||||||
package signer
|
package signer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"context"
|
||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
"context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StdIOUI struct {
|
type StdIOUI struct {
|
||||||
|
|
@ -114,3 +113,9 @@ func (ui *StdIOUI) ShowInfo(message string) {
|
||||||
log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
|
log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
|
err := ui.dispatch("OnApprovedTx", tx, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Accounts []Account
|
type Accounts []Account
|
||||||
|
|
@ -48,7 +50,7 @@ func (a Account) String() string {
|
||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
// TransactionArg represents a Transaction for the signer.
|
// TransactionArg represents a Transaction for the signer.
|
||||||
type TransactionArg struct {
|
type TransactionArg struct {
|
||||||
To *common.MixedcaseAddress `json:"to"`
|
To *common.MixedcaseAddress `json:"to"`
|
||||||
|
|
@ -58,11 +60,38 @@ type TransactionArg struct {
|
||||||
Data hexutil.Bytes `json:"data"`
|
Data hexutil.Bytes `json:"data"`
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func (t TransactionArg) String() string {
|
// SendTxArgs represents the arguments to submit a transaction
|
||||||
|
type SendTxArgs struct {
|
||||||
|
From common.MixedcaseAddress `json:"from"`
|
||||||
|
To *common.MixedcaseAddress `json:"to"`
|
||||||
|
Gas *hexutil.Big `json:"gas"`
|
||||||
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||||
|
Value *hexutil.Big `json:"value"`
|
||||||
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||||
|
// We accept "data" and "input" for backwards-compatibility reasons.
|
||||||
|
Data *hexutil.Bytes `json:"data"`
|
||||||
|
Input *hexutil.Bytes `json:"input"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SendTxArgs) String() string {
|
||||||
s, err := json.Marshal(t)
|
s, err := json.Marshal(t)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return string(s)
|
return string(s)
|
||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||||
|
var input []byte
|
||||||
|
if args.Data != nil {
|
||||||
|
input = *args.Data
|
||||||
|
} else if args.Input != nil {
|
||||||
|
input = *args.Input
|
||||||
|
}
|
||||||
|
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.NewTransaction(uint64(*args.Nonce), (*args.To).Address(), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), input)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue