all: add support for EIP-2718, EIP-2930 transactions (#21502)
This adds support for EIP-2718 typed transactions as well as EIP-2930 access list transactions (tx type 1). These EIPs are scheduled for the Berlin fork. There very few changes to existing APIs in core/types, and several new APIs to deal with access list transactions. In particular, there are two new constructor functions for transactions: types.NewTx and types.SignNewTx. Since the canonical encoding of typed transactions is not RLP-compatible, Transaction now has new methods for encoding and decoding: MarshalBinary and UnmarshalBinary. The existing EIP-155 signer does not support the new transaction types. All code dealing with transaction signatures should be updated to use the newer EIP-2930 signer. To make this easier for future updates, we have added new constructor functions for types.Signer: types.LatestSigner and types.LatestSignerForChainID. This change also adds support for the YoloV3 testnet. Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Ryan Schneider <ryanleeschneider@gmail.com>
This commit is contained in:
@ -410,7 +410,7 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs
|
||||
log.Warn("Failed transaction sign attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
|
||||
return nil, err
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(signed)
|
||||
data, err := signed.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -748,12 +748,13 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
AccessList *types.AccessList `json:"accessList"`
|
||||
}
|
||||
|
||||
// ToMessage converts CallArgs to the Message type used by the core evm
|
||||
@ -780,18 +781,20 @@ func (args *CallArgs) ToMessage(globalGasCap uint64) types.Message {
|
||||
if args.GasPrice != nil {
|
||||
gasPrice = args.GasPrice.ToInt()
|
||||
}
|
||||
|
||||
value := new(big.Int)
|
||||
if args.Value != nil {
|
||||
value = args.Value.ToInt()
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if args.Data != nil {
|
||||
data = *args.Data
|
||||
}
|
||||
var accessList types.AccessList
|
||||
if args.AccessList != nil {
|
||||
accessList = *args.AccessList
|
||||
}
|
||||
|
||||
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false)
|
||||
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, accessList, false)
|
||||
return msg
|
||||
}
|
||||
|
||||
@ -869,13 +872,13 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
||||
evm.Cancel()
|
||||
}()
|
||||
|
||||
// Setup the gas pool (also for unmetered requests)
|
||||
// and apply the message.
|
||||
// Execute the message.
|
||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||
result, err := core.ApplyMessage(evm, msg, gp)
|
||||
if err := vmError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the timer caused an abort, return an appropriate error message
|
||||
if evm.Cancelled() {
|
||||
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout)
|
||||
@ -1200,33 +1203,43 @@ func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Bloc
|
||||
|
||||
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
|
||||
type RPCTransaction struct {
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber"`
|
||||
From common.Address `json:"from"`
|
||||
Gas hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
To *common.Address `json:"to"`
|
||||
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber"`
|
||||
From common.Address `json:"from"`
|
||||
Gas hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
To *common.Address `json:"to"`
|
||||
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Type hexutil.Uint64 `json:"type"`
|
||||
Accesses *types.AccessList `json:"accessList,omitempty"`
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||
// representation, with the given location metadata set (if available).
|
||||
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
// Determine the signer. For replay-protected transactions, use the most permissive
|
||||
// signer, because we assume that signers are backwards-compatible with old
|
||||
// transactions. For non-protected transactions, the homestead signer signer is used
|
||||
// because the return value of ChainId is zero for those transactions.
|
||||
var signer types.Signer
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
signer = types.LatestSignerForChainID(tx.ChainId())
|
||||
} else {
|
||||
signer = types.HomesteadSigner{}
|
||||
}
|
||||
|
||||
from, _ := types.Sender(signer, tx)
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
|
||||
result := &RPCTransaction{
|
||||
Type: hexutil.Uint64(tx.Type()),
|
||||
From: from,
|
||||
Gas: hexutil.Uint64(tx.Gas()),
|
||||
GasPrice: (*hexutil.Big)(tx.GasPrice()),
|
||||
@ -1244,6 +1257,11 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
||||
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
|
||||
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||
}
|
||||
if tx.Type() == types.AccessListTxType {
|
||||
al := tx.AccessList()
|
||||
result.Accesses = &al
|
||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1267,7 +1285,7 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By
|
||||
if index >= uint64(len(txs)) {
|
||||
return nil
|
||||
}
|
||||
blob, _ := rlp.EncodeToBytes(txs[index])
|
||||
blob, _ := txs[index].MarshalBinary()
|
||||
return blob
|
||||
}
|
||||
|
||||
@ -1285,11 +1303,15 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa
|
||||
type PublicTransactionPoolAPI struct {
|
||||
b Backend
|
||||
nonceLock *AddrLocker
|
||||
signer types.Signer
|
||||
}
|
||||
|
||||
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
|
||||
func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI {
|
||||
return &PublicTransactionPoolAPI{b, nonceLock}
|
||||
// The signer used by the API should always be the 'latest' known one because we expect
|
||||
// signers to be backwards-compatible with old transactions.
|
||||
signer := types.LatestSigner(b.ChainConfig())
|
||||
return &PublicTransactionPoolAPI{b, nonceLock, signer}
|
||||
}
|
||||
|
||||
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
|
||||
@ -1394,7 +1416,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
|
||||
}
|
||||
}
|
||||
// Serialize to RLP and return
|
||||
return rlp.EncodeToBytes(tx)
|
||||
return tx.MarshalBinary()
|
||||
}
|
||||
|
||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||
@ -1412,10 +1434,9 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
||||
}
|
||||
receipt := receipts[index]
|
||||
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
// Derive the sender.
|
||||
bigblock := new(big.Int).SetUint64(blockNumber)
|
||||
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
@ -1430,6 +1451,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
||||
"contractAddress": nil,
|
||||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
"type": hexutil.Uint(tx.Type()),
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
@ -1473,9 +1495,13 @@ type SendTxArgs struct {
|
||||
// newer name and should be preferred by clients.
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
Input *hexutil.Bytes `json:"input"`
|
||||
|
||||
// For non-legacy transactions
|
||||
AccessList *types.AccessList `json:"accessList,omitempty"`
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
}
|
||||
|
||||
// setDefaults is a helper function that fills in default values for unspecified tx fields.
|
||||
// setDefaults fills in default values for unspecified tx fields.
|
||||
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
if args.GasPrice == nil {
|
||||
price, err := b.SuggestPrice(ctx)
|
||||
@ -1509,6 +1535,7 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
return errors.New(`contract creation without any data provided`)
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate the gas usage if necessary.
|
||||
if args.Gas == nil {
|
||||
// For backwards-compatibility reason, we try both input and data
|
||||
@ -1518,11 +1545,12 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
input = args.Data
|
||||
}
|
||||
callArgs := CallArgs{
|
||||
From: &args.From, // From shouldn't be nil
|
||||
To: args.To,
|
||||
GasPrice: args.GasPrice,
|
||||
Value: args.Value,
|
||||
Data: input,
|
||||
From: &args.From, // From shouldn't be nil
|
||||
To: args.To,
|
||||
GasPrice: args.GasPrice,
|
||||
Value: args.Value,
|
||||
Data: input,
|
||||
AccessList: args.AccessList,
|
||||
}
|
||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||
estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap())
|
||||
@ -1532,9 +1560,15 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
args.Gas = &estimated
|
||||
log.Trace("Estimate gas usage automatically", "gas", args.Gas)
|
||||
}
|
||||
if args.ChainID == nil {
|
||||
id := (*hexutil.Big)(b.ChainConfig().ChainID)
|
||||
args.ChainID = id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// toTransaction converts the arguments to a transaction.
|
||||
// This assumes that setDefaults has been called.
|
||||
func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||
var input []byte
|
||||
if args.Input != nil {
|
||||
@ -1542,10 +1576,30 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||
} else if args.Data != nil {
|
||||
input = *args.Data
|
||||
}
|
||||
if args.To == nil {
|
||||
return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||
|
||||
var data types.TxData
|
||||
if args.AccessList == nil {
|
||||
data = &types.LegacyTx{
|
||||
To: args.To,
|
||||
Nonce: uint64(*args.Nonce),
|
||||
Gas: uint64(*args.Gas),
|
||||
GasPrice: (*big.Int)(args.GasPrice),
|
||||
Value: (*big.Int)(args.Value),
|
||||
Data: input,
|
||||
}
|
||||
} else {
|
||||
data = &types.AccessListTx{
|
||||
To: args.To,
|
||||
ChainID: (*big.Int)(args.ChainID),
|
||||
Nonce: uint64(*args.Nonce),
|
||||
Gas: uint64(*args.Gas),
|
||||
GasPrice: (*big.Int)(args.GasPrice),
|
||||
Value: (*big.Int)(args.Value),
|
||||
Data: input,
|
||||
AccessList: *args.AccessList,
|
||||
}
|
||||
}
|
||||
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||
return types.NewTx(data)
|
||||
}
|
||||
|
||||
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
|
||||
@ -1619,7 +1673,7 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen
|
||||
}
|
||||
// Assemble the transaction and obtain rlp
|
||||
tx := args.toTransaction()
|
||||
data, err := rlp.EncodeToBytes(tx)
|
||||
data, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1628,9 +1682,9 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen
|
||||
|
||||
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||
// The sender is responsible for signing the transaction and using the correct nonce.
|
||||
func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) {
|
||||
func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
|
||||
tx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
||||
if err := tx.UnmarshalBinary(input); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return SubmitTransaction(ctx, s.b, tx)
|
||||
@ -1691,7 +1745,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(tx)
|
||||
data, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1713,11 +1767,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
|
||||
}
|
||||
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||
for _, tx := range pending {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
from, _ := types.Sender(s.signer, tx)
|
||||
if _, exists := accounts[from]; exists {
|
||||
transactions = append(transactions, newRPCPendingTransaction(tx))
|
||||
}
|
||||
@ -1754,13 +1804,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
||||
return common.Hash{}, err
|
||||
}
|
||||
for _, p := range pending {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if p.Protected() {
|
||||
signer = types.NewEIP155Signer(p.ChainId())
|
||||
}
|
||||
wantSigHash := signer.Hash(matchTx)
|
||||
|
||||
if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash {
|
||||
wantSigHash := s.signer.Hash(matchTx)
|
||||
pFrom, err := types.Sender(s.signer, p)
|
||||
if err == nil && pFrom == sendArgs.From && s.signer.Hash(p) == wantSigHash {
|
||||
// Match. Re-sign and send the transaction.
|
||||
if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 {
|
||||
sendArgs.GasPrice = gasPrice
|
||||
@ -1778,7 +1824,6 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
||||
return signedTx.Hash(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash())
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user