aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ethapi/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ethapi/api.go')
-rw-r--r--internal/ethapi/api.go159
1 files changed, 105 insertions, 54 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index fe0ed8170..314086335 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -17,6 +17,7 @@
package ethapi
import (
+ "bytes"
"context"
"errors"
"fmt"
@@ -44,7 +45,6 @@ import (
)
const (
- defaultGas = 90000
defaultGasPrice = 50 * params.Shannon
)
@@ -333,28 +333,19 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
return fetchKeystore(s.am).Lock(addr) == nil
}
-// SendTransaction will create a transaction from the given arguments and
-// tries to sign it with the key associated with args.To. If the given passwd isn't
-// able to decrypt the key it fails.
-func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
+// signTransactions sets defaults and signs the given transaction
+// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
+// and release it after the transaction has been submitted to the tx pool
+func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
// Look up the wallet containing the requested signer
account := accounts.Account{Address: args.From}
-
wallet, err := s.am.Find(account)
if err != nil {
- return common.Hash{}, err
- }
-
- if args.Nonce == nil {
- // Hold the addresse's mutex around signing to prevent concurrent assignment of
- // the same nonce to multiple accounts.
- s.nonceLock.LockAddr(args.From)
- defer s.nonceLock.UnlockAddr(args.From)
+ return nil, err
}
-
// Set some sanity defaults and terminate on failure
if err := args.setDefaults(ctx, s.b); err != nil {
- return common.Hash{}, err
+ return nil, err
}
// Assemble the transaction and sign with the wallet
tx := args.toTransaction()
@@ -363,13 +354,53 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
chainID = config.ChainId
}
- signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
+ return wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
+}
+
+// SendTransaction will create a transaction from the given arguments and
+// tries to sign it with the key associated with args.To. If the given passwd isn't
+// able to decrypt the key it fails.
+func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
+ if args.Nonce == nil {
+ // Hold the addresse's mutex around signing to prevent concurrent assignment of
+ // the same nonce to multiple accounts.
+ s.nonceLock.LockAddr(args.From)
+ defer s.nonceLock.UnlockAddr(args.From)
+ }
+ signed, err := s.signTransaction(ctx, args, passwd)
if err != nil {
return common.Hash{}, err
}
return submitTransaction(ctx, s.b, signed)
}
+// SignTransaction will create a transaction from the given arguments and
+// tries to sign it with the key associated with args.To. If the given passwd isn't
+// able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast
+// to other nodes
+func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs, passwd string) (*SignTransactionResult, error) {
+ // No need to obtain the noncelock mutex, since we won't be sending this
+ // tx into the transaction pool, but right back to the user
+ if args.Gas == nil {
+ return nil, fmt.Errorf("gas not specified")
+ }
+ if args.GasPrice == nil {
+ return nil, fmt.Errorf("gasPrice not specified")
+ }
+ if args.Nonce == nil {
+ return nil, fmt.Errorf("nonce not specified")
+ }
+ signed, err := s.signTransaction(ctx, args, passwd)
+ if err != nil {
+ return nil, err
+ }
+ data, err := rlp.EncodeToBytes(signed)
+ if err != nil {
+ return nil, err
+ }
+ return &SignTransactionResult{data, signed}, nil
+}
+
// signHash is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
@@ -574,18 +605,18 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
type CallArgs struct {
From common.Address `json:"from"`
To *common.Address `json:"to"`
- Gas hexutil.Big `json:"gas"`
+ Gas hexutil.Uint64 `json:"gas"`
GasPrice hexutil.Big `json:"gasPrice"`
Value hexutil.Big `json:"value"`
Data hexutil.Bytes `json:"data"`
}
-func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, bool, error) {
+func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, uint64, bool, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
- return nil, common.Big0, false, err
+ return nil, 0, false, err
}
// Set sender address or use a default if none specified
addr := args.From
@@ -597,9 +628,9 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
}
}
// Set default gas & gas price if none were set
- gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
- if gas.Sign() == 0 {
- gas = big.NewInt(50000000)
+ gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
+ if gas == 0 {
+ gas = 50000000
}
if gasPrice.Sign() == 0 {
gasPrice = new(big.Int).SetUint64(defaultGasPrice)
@@ -623,7 +654,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Get a new instance of the EVM.
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
if err != nil {
- return nil, common.Big0, false, err
+ return nil, 0, false, err
}
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
@@ -634,10 +665,10 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Setup the gas pool (also for unmetered requests)
// and apply the message.
- gp := new(core.GasPool).AddGas(math.MaxBig256)
+ gp := new(core.GasPool).AddGas(math.MaxUint64)
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
if err := vmError(); err != nil {
- return nil, common.Big0, false, err
+ return nil, 0, false, err
}
return res, gas, failed, err
}
@@ -651,28 +682,29 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr r
// EstimateGas returns an estimate of the amount of gas needed to execute the
// given transaction against the current pending block.
-func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*hexutil.Big, error) {
- // Determine the lowest and highest possible gas limits to binary search in between
+func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
+ // Binary search the gas requirement, as it may be higher than the amount used
var (
lo uint64 = params.TxGas - 1
hi uint64
cap uint64
)
- if (*big.Int)(&args.Gas).Uint64() >= params.TxGas {
- hi = (*big.Int)(&args.Gas).Uint64()
+ if uint64(args.Gas) >= params.TxGas {
+ hi = uint64(args.Gas)
} else {
// Retrieve the current pending block to act as the gas ceiling
block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber)
if err != nil {
- return nil, err
+ return 0, err
}
- hi = block.GasLimit().Uint64()
+ hi = block.GasLimit()
}
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) bool {
- (*big.Int)(&args.Gas).SetUint64(gas)
+ args.Gas = hexutil.Uint64(gas)
+
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
if err != nil || failed {
return false
@@ -691,17 +723,17 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
if !executable(hi) {
- return nil, fmt.Errorf("gas required exceeds allowance or always failing transaction")
+ return 0, fmt.Errorf("gas required exceeds allowance or always failing transaction")
}
}
- return (*hexutil.Big)(new(big.Int).SetUint64(hi)), nil
+ return hexutil.Uint64(hi), nil
}
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
type ExecutionResult struct {
- Gas *big.Int `json:"gas"`
+ Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`
StructLogs []StructLogRes `json:"structLogs"`
@@ -776,9 +808,9 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
"difficulty": (*hexutil.Big)(head.Difficulty),
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
"extraData": hexutil.Bytes(head.Extra),
- "size": hexutil.Uint64(uint64(b.Size().Int64())),
- "gasLimit": (*hexutil.Big)(head.GasLimit),
- "gasUsed": (*hexutil.Big)(head.GasUsed),
+ "size": hexutil.Uint64(b.Size()),
+ "gasLimit": hexutil.Uint64(head.GasLimit),
+ "gasUsed": hexutil.Uint64(head.GasUsed),
"timestamp": (*hexutil.Big)(head.Time),
"transactionsRoot": head.TxHash,
"receiptsRoot": head.ReceiptHash,
@@ -821,7 +853,7 @@ type RPCTransaction struct {
BlockHash common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
- Gas *hexutil.Big `json:"gas"`
+ Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
@@ -846,7 +878,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
result := &RPCTransaction{
From: from,
- Gas: (*hexutil.Big)(tx.Gas()),
+ Gas: hexutil.Uint64(tx.Gas()),
GasPrice: (*hexutil.Big)(tx.GasPrice()),
Hash: tx.Hash(),
Input: hexutil.Bytes(tx.Data()),
@@ -1003,9 +1035,12 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
if tx == nil {
- return nil, nil
+ return nil, errors.New("unknown transaction")
}
receipt, _, _, _ := core.GetReceipt(s.b.ChainDb(), hash) // Old receipts don't have the lookup data available
+ if receipt == nil {
+ return nil, errors.New("unknown receipt")
+ }
var signer types.Signer = types.FrontierSigner{}
if tx.Protected() {
@@ -1020,8 +1055,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[
"transactionIndex": hexutil.Uint64(index),
"from": from,
"to": tx.To(),
- "gasUsed": (*hexutil.Big)(receipt.GasUsed),
- "cumulativeGasUsed": (*hexutil.Big)(receipt.CumulativeGasUsed),
+ "gasUsed": hexutil.Uint64(receipt.GasUsed),
+ "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
@@ -1064,17 +1099,21 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
type SendTxArgs struct {
From common.Address `json:"from"`
To *common.Address `json:"to"`
- Gas *hexutil.Big `json:"gas"`
+ Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Value *hexutil.Big `json:"value"`
- Data hexutil.Bytes `json:"data"`
Nonce *hexutil.Uint64 `json:"nonce"`
+ // We accept "data" and "input" for backwards-compatibility reasons. "input" is the
+ // newer name and should be preferred by clients.
+ Data *hexutil.Bytes `json:"data"`
+ Input *hexutil.Bytes `json:"input"`
}
// setDefaults is a helper function that fills in default values for unspecified tx fields.
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
if args.Gas == nil {
- args.Gas = (*hexutil.Big)(big.NewInt(defaultGas))
+ args.Gas = new(hexutil.Uint64)
+ *(*uint64)(args.Gas) = 90000
}
if args.GasPrice == nil {
price, err := b.SuggestPrice(ctx)
@@ -1093,14 +1132,23 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
}
args.Nonce = (*hexutil.Uint64)(&nonce)
}
+ if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
+ return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
+ }
return nil
}
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), args.Data)
+ return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
}
- return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
+ return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
}
// submitTransaction is a helper function that submits tx to txPool and logs a message.
@@ -1204,11 +1252,14 @@ type SignTransactionResult struct {
// The node needs to have the private key of the account corresponding with
// the given from address and it needs to be unlocked.
func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) {
+ if args.Gas == nil {
+ return nil, fmt.Errorf("gas not specified")
+ }
+ if args.GasPrice == nil {
+ return nil, fmt.Errorf("gasPrice not specified")
+ }
if args.Nonce == nil {
- // Hold the addresse's mutex around signing to prevent concurrent assignment of
- // the same nonce to multiple accounts.
- s.nonceLock.LockAddr(args.From)
- defer s.nonceLock.UnlockAddr(args.From)
+ return nil, fmt.Errorf("nonce not specified")
}
if err := args.setDefaults(ctx, s.b); err != nil {
return nil, err
@@ -1248,7 +1299,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
// Resend accepts an existing transaction and a new gas price and limit. It will remove
// the given transaction from the pool and reinsert it with the new gas price and limit.
-func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxArgs, gasPrice, gasLimit *hexutil.Big) (common.Hash, error) {
+func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
if sendArgs.Nonce == nil {
return common.Hash{}, fmt.Errorf("missing transaction nonce in transaction spec")
}