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:
@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas, _ := IntrinsicGas(data, false, false, false)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false, false)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
|
||||
gen.AddTx(tx)
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{0x00})
|
||||
@ -839,7 +839,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
// Create two transactions shared between the chains:
|
||||
@ -944,7 +944,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
@ -998,7 +998,7 @@ func TestLogRebirth(t *testing.T) {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
)
|
||||
@ -1062,7 +1062,7 @@ func TestSideLogRebirth(t *testing.T) {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
)
|
||||
|
||||
@ -1135,7 +1135,7 @@ func TestReorgSideEvent(t *testing.T) {
|
||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
@ -1295,7 +1295,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainID))
|
||||
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1307,7 +1307,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainID))
|
||||
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1345,7 +1345,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||
}
|
||||
)
|
||||
if i == 0 {
|
||||
tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
|
||||
tx, err = basicTx(types.LatestSigner(config))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1385,7 +1385,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
@ -2078,7 +2078,7 @@ func TestTransactionIndices(t *testing.T) {
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
height := uint64(128)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) {
|
||||
@ -2205,7 +2205,7 @@ func TestSkipStaleTxIndicesInFastSync(t *testing.T) {
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
height := uint64(128)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), func(i int, block *BlockGen) {
|
||||
@ -3030,3 +3030,81 @@ func TestInitThenFailCreateContract(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEIP2718Transition tests that an EIP-2718 transaction will be accepted
|
||||
// after the fork block has passed. This is verified by sending an EIP-2930
|
||||
// access list transaction, which specifies a single slot access, and then
|
||||
// checking that the gas usage of a hot SLOAD and a cold SLOAD are calculated
|
||||
// correctly.
|
||||
func TestEIP2718Transition(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine = ethash.NewFaker()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.YoloV3ChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
aa: {
|
||||
Code: []byte{
|
||||
byte(vm.PC),
|
||||
byte(vm.PC),
|
||||
byte(vm.SLOAD),
|
||||
byte(vm.SLOAD),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
|
||||
// One transaction to 0xAAAA
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasPrice: big.NewInt(1),
|
||||
AccessList: types.AccessList{{
|
||||
Address: aa,
|
||||
StorageKeys: []common.Hash{{0}},
|
||||
}},
|
||||
})
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block := chain.GetBlockByNumber(1)
|
||||
|
||||
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
|
||||
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
|
||||
if block.GasUsed() != expected {
|
||||
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,11 @@
|
||||
|
||||
package core
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||
@ -63,4 +67,8 @@ var (
|
||||
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
||||
// than required to start the invocation.
|
||||
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
||||
|
||||
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
|
||||
// current network configuration.
|
||||
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
||||
)
|
||||
|
@ -377,11 +377,11 @@ func DefaultGoerliGenesisBlock() *Genesis {
|
||||
}
|
||||
|
||||
func DefaultYoloV3GenesisBlock() *Genesis {
|
||||
// Full genesis: https://gist.github.com/holiman/b2c32a05ff2e2712e11c0787d987d46f
|
||||
// Full genesis: https://gist.github.com/holiman/c6ed9269dce28304ad176314caa75e97
|
||||
return &Genesis{
|
||||
Config: params.YoloV3ChainConfig,
|
||||
Timestamp: 0x60117f8b,
|
||||
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
Timestamp: 0x6027dd2e,
|
||||
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000001041afbcb359d5a8dc58c15b2ff51354ff8a217d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 0x47b760,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(yoloV3AllocData),
|
||||
|
@ -25,5 +25,4 @@ const mainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'
|
||||
const ropstenAllocData = "\xf9\x03\xa4\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x80\xc2\v\x80\xc2\f\x80\xc2\r\x80\xc2\x0e\x80\xc2\x0f\x80\xc2\x10\x80\xc2\x11\x80\xc2\x12\x80\xc2\x13\x80\xc2\x14\x80\xc2\x15\x80\xc2\x16\x80\xc2\x17\x80\xc2\x18\x80\xc2\x19\x80\xc2\x1a\x80\xc2\x1b\x80\xc2\x1c\x80\xc2\x1d\x80\xc2\x1e\x80\xc2\x1f\x80\xc2 \x80\xc2!\x80\xc2\"\x80\xc2#\x80\xc2$\x80\xc2%\x80\xc2&\x80\xc2'\x80\xc2(\x80\xc2)\x80\xc2*\x80\xc2+\x80\xc2,\x80\xc2-\x80\xc2.\x80\xc2/\x80\xc20\x80\xc21\x80\xc22\x80\xc23\x80\xc24\x80\xc25\x80\xc26\x80\xc27\x80\xc28\x80\xc29\x80\xc2:\x80\xc2;\x80\xc2<\x80\xc2=\x80\xc2>\x80\xc2?\x80\xc2@\x80\xc2A\x80\xc2B\x80\xc2C\x80\xc2D\x80\xc2E\x80\xc2F\x80\xc2G\x80\xc2H\x80\xc2I\x80\xc2J\x80\xc2K\x80\xc2L\x80\xc2M\x80\xc2N\x80\xc2O\x80\xc2P\x80\xc2Q\x80\xc2R\x80\xc2S\x80\xc2T\x80\xc2U\x80\xc2V\x80\xc2W\x80\xc2X\x80\xc2Y\x80\xc2Z\x80\xc2[\x80\xc2\\\x80\xc2]\x80\xc2^\x80\xc2_\x80\xc2`\x80\xc2a\x80\xc2b\x80\xc2c\x80\xc2d\x80\xc2e\x80\xc2f\x80\xc2g\x80\xc2h\x80\xc2i\x80\xc2j\x80\xc2k\x80\xc2l\x80\xc2m\x80\xc2n\x80\xc2o\x80\xc2p\x80\xc2q\x80\xc2r\x80\xc2s\x80\xc2t\x80\xc2u\x80\xc2v\x80\xc2w\x80\xc2x\x80\xc2y\x80\xc2z\x80\xc2{\x80\xc2|\x80\xc2}\x80\xc2~\x80\xc2\u007f\x80\u00c1\x80\x80\u00c1\x81\x80\u00c1\x82\x80\u00c1\x83\x80\u00c1\x84\x80\u00c1\x85\x80\u00c1\x86\x80\u00c1\x87\x80\u00c1\x88\x80\u00c1\x89\x80\u00c1\x8a\x80\u00c1\x8b\x80\u00c1\x8c\x80\u00c1\x8d\x80\u00c1\x8e\x80\u00c1\x8f\x80\u00c1\x90\x80\u00c1\x91\x80\u00c1\x92\x80\u00c1\x93\x80\u00c1\x94\x80\u00c1\x95\x80\u00c1\x96\x80\u00c1\x97\x80\u00c1\x98\x80\u00c1\x99\x80\u00c1\x9a\x80\u00c1\x9b\x80\u00c1\x9c\x80\u00c1\x9d\x80\u00c1\x9e\x80\u00c1\x9f\x80\u00c1\xa0\x80\u00c1\xa1\x80\u00c1\xa2\x80\u00c1\xa3\x80\u00c1\xa4\x80\u00c1\xa5\x80\u00c1\xa6\x80\u00c1\xa7\x80\u00c1\xa8\x80\u00c1\xa9\x80\u00c1\xaa\x80\u00c1\xab\x80\u00c1\xac\x80\u00c1\xad\x80\u00c1\xae\x80\u00c1\xaf\x80\u00c1\xb0\x80\u00c1\xb1\x80\u00c1\xb2\x80\u00c1\xb3\x80\u00c1\xb4\x80\u00c1\xb5\x80\u00c1\xb6\x80\u00c1\xb7\x80\u00c1\xb8\x80\u00c1\xb9\x80\u00c1\xba\x80\u00c1\xbb\x80\u00c1\xbc\x80\u00c1\xbd\x80\u00c1\xbe\x80\u00c1\xbf\x80\u00c1\xc0\x80\u00c1\xc1\x80\u00c1\u0080\u00c1\u00c0\u00c1\u0100\u00c1\u0140\u00c1\u0180\u00c1\u01c0\u00c1\u0200\u00c1\u0240\u00c1\u0280\u00c1\u02c0\u00c1\u0300\u00c1\u0340\u00c1\u0380\u00c1\u03c0\u00c1\u0400\u00c1\u0440\u00c1\u0480\u00c1\u04c0\u00c1\u0500\u00c1\u0540\u00c1\u0580\u00c1\u05c0\u00c1\u0600\u00c1\u0640\u00c1\u0680\u00c1\u06c0\u00c1\u0700\u00c1\u0740\u00c1\u0780\u00c1\u07c0\u00c1\xe0\x80\u00c1\xe1\x80\u00c1\xe2\x80\u00c1\xe3\x80\u00c1\xe4\x80\u00c1\xe5\x80\u00c1\xe6\x80\u00c1\xe7\x80\u00c1\xe8\x80\u00c1\xe9\x80\u00c1\xea\x80\u00c1\xeb\x80\u00c1\xec\x80\u00c1\xed\x80\u00c1\xee\x80\u00c1\xef\x80\u00c1\xf0\x80\u00c1\xf1\x80\u00c1\xf2\x80\u00c1\xf3\x80\u00c1\xf4\x80\u00c1\xf5\x80\u00c1\xf6\x80\u00c1\xf7\x80\u00c1\xf8\x80\u00c1\xf9\x80\u00c1\xfa\x80\u00c1\xfb\x80\u00c1\xfc\x80\u00c1\xfd\x80\u00c1\xfe\x80\u00c1\xff\x80\u3507KT\xa8\xbd\x15)f\xd6?pk\xae\x1f\xfe\xb0A\x19!\xe5\x8d\f\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00"
|
||||
const rinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
const goerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00"
|
||||
const yoloV3AllocData = "\xf9\x05\x01\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x0e\x89\xe2\xae\xdb\x1c\xfc\u06d4$\xd4\x1a\x1f!\x8fA2s\x81r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94`\xad\xc0\xf8\x9aA\xaf#|\xe75T\xed\xe1p\xd73\xec\x14\xe0\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8a\x8e\xaf\xb1\xcfb\xbf\xbe\xb1t\x17i\xda\xe1\xa9\xddG\x99a\x92\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8b\xa1\xf1\tU\x1b\xd42\x800\x12dZ\xc16\xdd\xd6M\xbar\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xb0*.\xda\x1b1\u007f\xbd\x16v\x01(\x83k\n\u015bV\x0e\x9d\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
const yoloV3AllocData = "\xf9\x05o\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x0e\x89\xe2\xae\xdb\x1c\xfc\u06d4$\xd4\x1a\x1f!\x8fA2s\x81r\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x10A\xaf\xbc\xb3Y\u0568\xdcX\xc1[/\xf5\x13T\xff\x8a!}\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94`\xad\xc0\xf8\x9aA\xaf#|\xe75T\xed\xe1p\xd73\xec\x14\xe0\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8a\x8e\xaf\xb1\xcfb\xbf\xbe\xb1t\x17i\xda\xe1\xa9\xddG\x99a\x92\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\x8b\xa1\xf1\tU\x1b\xd42\x800\x12dZ\xc16\xdd\xd6M\xbar\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xb0*.\xda\x1b1\u007f\xbd\x16v\x01(\x83k\n\u015bV\x0e\x9d\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x94\xdf\n\x88\xb2\xb6\x8cg7\x13\xa8\xec\x82`\x03go'.5s\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
@ -983,6 +983,32 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// PrepareAccessList handles the preparatory steps for executing a state transition with
|
||||
// regards to both EIP-2929 and EIP-2930:
|
||||
//
|
||||
// - Add sender to access list (2929)
|
||||
// - Add destination to access list (2929)
|
||||
// - Add precompiles to access list (2929)
|
||||
// - Add the contents of the optional tx access list (2930)
|
||||
//
|
||||
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
|
||||
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||
s.AddAddressToAccessList(sender)
|
||||
if dst != nil {
|
||||
s.AddAddressToAccessList(*dst)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
s.AddAddressToAccessList(addr)
|
||||
}
|
||||
for _, el := range list {
|
||||
s.AddAddressToAccessList(el.Address)
|
||||
for _, key := range el.StorageKeys {
|
||||
s.AddSlotToAccessList(el.Address, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddAddressToAccessList adds the given address to the access list
|
||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||
if s.accessList.AddAddress(addr) {
|
||||
|
@ -19,7 +19,6 @@ package core
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -50,8 +49,11 @@ func newStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine conse
|
||||
// only goal is to pre-cache transaction signatures and state trie nodes.
|
||||
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) {
|
||||
var (
|
||||
header = block.Header()
|
||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
||||
header = block.Header()
|
||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
||||
blockContext = NewEVMBlockContext(header, p.bc, nil)
|
||||
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||
signer = types.MakeSigner(p.config, header.Number)
|
||||
)
|
||||
// Iterate over and process the individual transactions
|
||||
byzantium := p.config.IsByzantium(block.Number())
|
||||
@ -60,9 +62,13 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||
if interrupt != nil && atomic.LoadUint32(interrupt) == 1 {
|
||||
return
|
||||
}
|
||||
// Block precaching permitted to continue, execute the transaction
|
||||
// Convert the transaction into an executable message and pre-cache its sender
|
||||
msg, err := tx.AsMessage(signer)
|
||||
if err != nil {
|
||||
return // Also invalid block, bail out
|
||||
}
|
||||
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
||||
if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg); err != nil {
|
||||
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
|
||||
return // Ugh, something went horribly wrong, bail out
|
||||
}
|
||||
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
|
||||
@ -79,17 +85,10 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||
// precacheTransaction attempts to apply a transaction to the given state database
|
||||
// and uses the input parameters for its environment. The goal is not to execute
|
||||
// the transaction successfully, rather to warm up touched data slots.
|
||||
func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gaspool *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) error {
|
||||
// Convert the transaction into an executable message and pre-cache its sender
|
||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create the EVM and execute the transaction
|
||||
context := NewEVMBlockContext(header, bc, author)
|
||||
txContext := NewEVMTxContext(msg)
|
||||
vm := vm.NewEVM(context, txContext, statedb, config, cfg)
|
||||
|
||||
_, err = ApplyMessage(vm, msg, gaspool)
|
||||
func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error {
|
||||
// Update the evm with the new transaction context.
|
||||
evm.Reset(NewEVMTxContext(msg), statedb)
|
||||
// Add addresses to access list if applicable
|
||||
_, err := ApplyMessage(evm, msg, gaspool)
|
||||
return err
|
||||
}
|
||||
|
@ -90,28 +90,17 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
}
|
||||
|
||||
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
|
||||
// Create a new context to be used in the EVM environment
|
||||
// Create a new context to be used in the EVM environment.
|
||||
txContext := NewEVMTxContext(msg)
|
||||
// Add addresses to access list if applicable
|
||||
if config.IsYoloV3(header.Number) {
|
||||
statedb.AddAddressToAccessList(msg.From())
|
||||
if dst := msg.To(); dst != nil {
|
||||
statedb.AddAddressToAccessList(*dst)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range evm.ActivePrecompiles() {
|
||||
statedb.AddAddressToAccessList(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the evm with the new transaction context.
|
||||
evm.Reset(txContext, statedb)
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
|
||||
// Apply the transaction to the current state (included in the env).
|
||||
result, err := ApplyMessage(evm, msg, gp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Update the state with pending changes
|
||||
|
||||
// Update the state with pending changes.
|
||||
var root []byte
|
||||
if config.IsByzantium(header.Number) {
|
||||
statedb.Finalise(true)
|
||||
@ -120,22 +109,28 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
|
||||
}
|
||||
*usedGas += result.UsedGas
|
||||
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
||||
receipt := types.NewReceipt(root, result.Failed(), *usedGas)
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||
// by the tx.
|
||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
||||
if result.Failed() {
|
||||
receipt.Status = types.ReceiptStatusFailed
|
||||
} else {
|
||||
receipt.Status = types.ReceiptStatusSuccessful
|
||||
}
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = result.UsedGas
|
||||
// if the transaction created a contract, store the creation address in the receipt.
|
||||
|
||||
// If the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To() == nil {
|
||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||
}
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
|
||||
return receipt, err
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
@ -67,6 +68,7 @@ type Message interface {
|
||||
Nonce() uint64
|
||||
CheckNonce() bool
|
||||
Data() []byte
|
||||
AccessList() types.AccessList
|
||||
}
|
||||
|
||||
// ExecutionResult includes all output after executing given evm
|
||||
@ -105,10 +107,10 @@ func (result *ExecutionResult) Revert() []byte {
|
||||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if contractCreation && isHomestead {
|
||||
if isContractCreation && isHomestead {
|
||||
gas = params.TxGasContractCreation
|
||||
} else {
|
||||
gas = params.TxGas
|
||||
@ -138,6 +140,10 @@ func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 boo
|
||||
}
|
||||
gas += z * params.TxDataZeroGas
|
||||
}
|
||||
if accessList != nil {
|
||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
@ -238,7 +244,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
contractCreation := msg.To() == nil
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -251,6 +257,12 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) {
|
||||
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
|
||||
}
|
||||
|
||||
// Set up the initial access list.
|
||||
if st.evm.ChainConfig().IsYoloV3(st.evm.Context.BlockNumber) {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList())
|
||||
}
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
|
@ -229,6 +229,7 @@ type TxPool struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingNonces *txNoncer // Pending state tracking virtual nonces
|
||||
@ -268,7 +269,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||
config: config,
|
||||
chainconfig: chainconfig,
|
||||
chain: chain,
|
||||
signer: types.NewEIP155Signer(chainconfig.ChainID),
|
||||
signer: types.LatestSigner(chainconfig),
|
||||
pending: make(map[common.Address]*txList),
|
||||
queue: make(map[common.Address]*txList),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
@ -522,6 +523,10 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
|
||||
// validateTx checks whether a transaction is valid according to the consensus
|
||||
// rules and adheres to some heuristic limits of the local node (price and size).
|
||||
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
// Accept only legacy transactions until EIP-2718/2930 activates.
|
||||
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
// Reject transactions over defined size to prevent DOS attacks
|
||||
if uint64(tx.Size()) > txMaxSize {
|
||||
return ErrOversizedData
|
||||
@ -535,7 +540,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
if pool.currentMaxGas < tx.Gas() {
|
||||
return ErrGasLimit
|
||||
}
|
||||
// Make sure the transaction is signed properly
|
||||
// Make sure the transaction is signed properly.
|
||||
from, err := types.Sender(pool.signer, tx)
|
||||
if err != nil {
|
||||
return ErrInvalidSender
|
||||
@ -554,7 +559,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
return ErrInsufficientFunds
|
||||
}
|
||||
// Ensure the transaction has more gas than the basic tx fee.
|
||||
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul)
|
||||
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1199,6 +1204,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||
// Update all fork indicator by next pending block number.
|
||||
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
|
||||
pool.istanbul = pool.chainconfig.IsIstanbul(next)
|
||||
pool.eip2718 = pool.chainconfig.IsYoloV3(next)
|
||||
}
|
||||
|
||||
// promoteExecutables moves transactions that have become processable from the
|
||||
|
115
core/types/access_list_tx.go
Normal file
115
core/types/access_list_tx.go
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
|
||||
|
||||
// AccessList is an EIP-2930 access list.
|
||||
type AccessList []AccessTuple
|
||||
|
||||
// AccessTuple is the element type of an access list.
|
||||
type AccessTuple struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
|
||||
// StorageKeys returns the total number of storage keys in the access list.
|
||||
func (al AccessList) StorageKeys() int {
|
||||
sum := 0
|
||||
for _, tuple := range al {
|
||||
sum += len(tuple.StorageKeys)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// AccessListTx is the data of EIP-2930 access list transactions.
|
||||
type AccessListTx struct {
|
||||
ChainID *big.Int // destination chain ID
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
AccessList AccessList // EIP-2930 access list
|
||||
V, R, S *big.Int // signature values
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *AccessListTx) copy() TxData {
|
||||
cpy := &AccessListTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are copied below.
|
||||
AccessList: make(AccessList, len(tx.AccessList)),
|
||||
Value: new(big.Int),
|
||||
ChainID: new(big.Int),
|
||||
GasPrice: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
copy(cpy.AccessList, tx.AccessList)
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.ChainID != nil {
|
||||
cpy.ChainID.Set(tx.ChainID)
|
||||
}
|
||||
if tx.GasPrice != nil {
|
||||
cpy.GasPrice.Set(tx.GasPrice)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *AccessListTx) protected() bool { return true }
|
||||
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value }
|
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
||||
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
|
||||
}
|
@ -23,15 +23,12 @@ import (
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -131,22 +128,6 @@ func (h *Header) SanityCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasherPool holds LegacyKeccak hashers.
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return sha3.NewLegacyKeccak256()
|
||||
},
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
defer hasherPool.Put(sha)
|
||||
sha.Reset()
|
||||
rlp.Encode(sha, x)
|
||||
sha.Read(h[:])
|
||||
return h
|
||||
}
|
||||
|
||||
// EmptyBody returns true if there is no additional 'body' to complete the header
|
||||
// that is: no transactions and no uncles.
|
||||
func (h *Header) EmptyBody() bool {
|
||||
@ -221,7 +202,7 @@ type storageblock struct {
|
||||
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
|
||||
// are ignored and set to values derived from the given txs, uncles
|
||||
// and receipts.
|
||||
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher Hasher) *Block {
|
||||
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block {
|
||||
b := &Block{header: CopyHeader(header), td: new(big.Int)}
|
||||
|
||||
// TODO: panic if len(txs) != len(receipts)
|
||||
|
@ -59,6 +59,66 @@ func TestBlockEncoding(t *testing.T) {
|
||||
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
|
||||
check("len(Transactions)", len(block.Transactions()), 1)
|
||||
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
|
||||
ourBlockEnc, err := rlp.EncodeToBytes(&block)
|
||||
if err != nil {
|
||||
t.Fatal("encode error: ", err)
|
||||
}
|
||||
if !bytes.Equal(ourBlockEnc, blockEnc) {
|
||||
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP2718BlockEncoding(t *testing.T) {
|
||||
blockEnc := common.FromHex("f90319f90211a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0")
|
||||
var block Block
|
||||
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
|
||||
t.Fatal("decode error: ", err)
|
||||
}
|
||||
|
||||
check := func(f string, got, want interface{}) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||
}
|
||||
}
|
||||
check("Difficulty", block.Difficulty(), big.NewInt(131072))
|
||||
check("GasLimit", block.GasLimit(), uint64(3141592))
|
||||
check("GasUsed", block.GasUsed(), uint64(42000))
|
||||
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
|
||||
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
|
||||
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
|
||||
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
|
||||
check("Time", block.Time(), uint64(1426516743))
|
||||
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||
|
||||
// Create legacy tx.
|
||||
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
|
||||
tx1 := NewTx(&LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &to,
|
||||
Value: big.NewInt(10),
|
||||
Gas: 50000,
|
||||
GasPrice: big.NewInt(10),
|
||||
})
|
||||
sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")
|
||||
tx1, _ = tx1.WithSignature(HomesteadSigner{}, sig)
|
||||
|
||||
// Create ACL tx.
|
||||
addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
tx2 := NewTx(&AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
To: &to,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}},
|
||||
})
|
||||
sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401")
|
||||
tx2, _ = tx2.WithSignature(NewEIP2930Signer(big.NewInt(1)), sig2)
|
||||
|
||||
check("len(Transactions)", len(block.Transactions()), 2)
|
||||
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
|
||||
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
|
||||
check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType))
|
||||
|
||||
ourBlockEnc, err := rlp.EncodeToBytes(&block)
|
||||
if err != nil {
|
||||
@ -121,7 +181,7 @@ func makeBenchBlock() *Block {
|
||||
key, _ = crypto.GenerateKey()
|
||||
txs = make([]*Transaction, 70)
|
||||
receipts = make([]*Receipt, len(txs))
|
||||
signer = NewEIP155Signer(params.TestChainConfig.ChainID)
|
||||
signer = LatestSigner(params.TestChainConfig)
|
||||
uncles = make([]*Header, 3)
|
||||
)
|
||||
header := &Header{
|
||||
|
@ -1,58 +0,0 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type DerivableList interface {
|
||||
Len() int
|
||||
GetRlp(i int) []byte
|
||||
}
|
||||
|
||||
// Hasher is the tool used to calculate the hash of derivable list.
|
||||
type Hasher interface {
|
||||
Reset()
|
||||
Update([]byte, []byte)
|
||||
Hash() common.Hash
|
||||
}
|
||||
|
||||
func DeriveSha(list DerivableList, hasher Hasher) common.Hash {
|
||||
hasher.Reset()
|
||||
|
||||
// StackTrie requires values to be inserted in increasing
|
||||
// hash order, which is not the order that `list` provides
|
||||
// hashes in. This insertion sequence ensures that the
|
||||
// order is correct.
|
||||
|
||||
var buf []byte
|
||||
for i := 1; i < list.Len() && i <= 0x7f; i++ {
|
||||
buf = rlp.AppendUint64(buf[:0], uint64(i))
|
||||
hasher.Update(buf, list.GetRlp(i))
|
||||
}
|
||||
if list.Len() > 0 {
|
||||
buf = rlp.AppendUint64(buf[:0], 0)
|
||||
hasher.Update(buf, list.GetRlp(0))
|
||||
}
|
||||
for i := 0x80; i < list.Len(); i++ {
|
||||
buf = rlp.AppendUint64(buf[:0], uint64(i))
|
||||
hasher.Update(buf, list.GetRlp(i))
|
||||
}
|
||||
return hasher.Hash()
|
||||
}
|
43
core/types/gen_access_tuple.go
Normal file
43
core/types/gen_access_tuple.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (a AccessTuple) MarshalJSON() ([]byte, error) {
|
||||
type AccessTuple struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
var enc AccessTuple
|
||||
enc.Address = a.Address
|
||||
enc.StorageKeys = a.StorageKeys
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (a *AccessTuple) UnmarshalJSON(input []byte) error {
|
||||
type AccessTuple struct {
|
||||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
|
||||
}
|
||||
var dec AccessTuple
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil {
|
||||
return errors.New("missing required field 'address' for AccessTuple")
|
||||
}
|
||||
a.Address = *dec.Address
|
||||
if dec.StorageKeys == nil {
|
||||
return errors.New("missing required field 'storageKeys' for AccessTuple")
|
||||
}
|
||||
a.StorageKeys = dec.StorageKeys
|
||||
return nil
|
||||
}
|
@ -16,6 +16,7 @@ var _ = (*receiptMarshaling)(nil)
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
type Receipt struct {
|
||||
Type hexutil.Uint64 `json:"type,omitempty"`
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
Status hexutil.Uint64 `json:"status"`
|
||||
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
@ -29,6 +30,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||
}
|
||||
var enc Receipt
|
||||
enc.Type = hexutil.Uint64(r.Type)
|
||||
enc.PostState = r.PostState
|
||||
enc.Status = hexutil.Uint64(r.Status)
|
||||
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
|
||||
@ -46,6 +48,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
type Receipt struct {
|
||||
Type *hexutil.Uint64 `json:"type,omitempty"`
|
||||
PostState *hexutil.Bytes `json:"root"`
|
||||
Status *hexutil.Uint64 `json:"status"`
|
||||
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
@ -62,6 +65,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Type != nil {
|
||||
r.Type = uint8(*dec.Type)
|
||||
}
|
||||
if dec.PostState != nil {
|
||||
r.PostState = *dec.PostState
|
||||
}
|
||||
|
@ -1,101 +0,0 @@
|
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*txdataMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
type txdata struct {
|
||||
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value" gencodec:"required"`
|
||||
Payload hexutil.Bytes `json:"input" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
}
|
||||
var enc txdata
|
||||
enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
|
||||
enc.Price = (*hexutil.Big)(t.Price)
|
||||
enc.GasLimit = hexutil.Uint64(t.GasLimit)
|
||||
enc.Recipient = t.Recipient
|
||||
enc.Amount = (*hexutil.Big)(t.Amount)
|
||||
enc.Payload = t.Payload
|
||||
enc.V = (*hexutil.Big)(t.V)
|
||||
enc.R = (*hexutil.Big)(t.R)
|
||||
enc.S = (*hexutil.Big)(t.S)
|
||||
enc.Hash = t.Hash
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
type txdata struct {
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value" gencodec:"required"`
|
||||
Payload *hexutil.Bytes `json:"input" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
}
|
||||
var dec txdata
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.AccountNonce == nil {
|
||||
return errors.New("missing required field 'nonce' for txdata")
|
||||
}
|
||||
t.AccountNonce = uint64(*dec.AccountNonce)
|
||||
if dec.Price == nil {
|
||||
return errors.New("missing required field 'gasPrice' for txdata")
|
||||
}
|
||||
t.Price = (*big.Int)(dec.Price)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gas' for txdata")
|
||||
}
|
||||
t.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.Recipient != nil {
|
||||
t.Recipient = dec.Recipient
|
||||
}
|
||||
if dec.Amount == nil {
|
||||
return errors.New("missing required field 'value' for txdata")
|
||||
}
|
||||
t.Amount = (*big.Int)(dec.Amount)
|
||||
if dec.Payload == nil {
|
||||
return errors.New("missing required field 'input' for txdata")
|
||||
}
|
||||
t.Payload = *dec.Payload
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for txdata")
|
||||
}
|
||||
t.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' for txdata")
|
||||
}
|
||||
t.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' for txdata")
|
||||
}
|
||||
t.S = (*big.Int)(dec.S)
|
||||
if dec.Hash != nil {
|
||||
t.Hash = dec.Hash
|
||||
}
|
||||
return nil
|
||||
}
|
112
core/types/hashing.go
Normal file
112
core/types/hashing.go
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
||||
}
|
||||
|
||||
// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
var encodeBufferPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
defer hasherPool.Put(sha)
|
||||
sha.Reset()
|
||||
rlp.Encode(sha, x)
|
||||
sha.Read(h[:])
|
||||
return h
|
||||
}
|
||||
|
||||
// prefixedRlpHash writes the prefix into the hasher before rlp-encoding the
|
||||
// given interface. It's used for typed transactions.
|
||||
func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
|
||||
sha := hasherPool.Get().(crypto.KeccakState)
|
||||
defer hasherPool.Put(sha)
|
||||
sha.Reset()
|
||||
sha.Write([]byte{prefix})
|
||||
rlp.Encode(sha, x)
|
||||
sha.Read(h[:])
|
||||
return h
|
||||
}
|
||||
|
||||
// TrieHasher is the tool used to calculate the hash of derivable list.
|
||||
// This is internal, do not use.
|
||||
type TrieHasher interface {
|
||||
Reset()
|
||||
Update([]byte, []byte)
|
||||
Hash() common.Hash
|
||||
}
|
||||
|
||||
// DerivableList is the input to DeriveSha.
|
||||
// It is implemented by the 'Transactions' and 'Receipts' types.
|
||||
// This is internal, do not use these methods.
|
||||
type DerivableList interface {
|
||||
Len() int
|
||||
EncodeIndex(int, *bytes.Buffer)
|
||||
}
|
||||
|
||||
func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
|
||||
buf.Reset()
|
||||
list.EncodeIndex(i, buf)
|
||||
// It's really unfortunate that we need to do perform this copy.
|
||||
// StackTrie holds onto the values until Hash is called, so the values
|
||||
// written to it must not alias.
|
||||
return common.CopyBytes(buf.Bytes())
|
||||
}
|
||||
|
||||
// DeriveSha creates the tree hashes of transactions and receipts in a block header.
|
||||
func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash {
|
||||
hasher.Reset()
|
||||
|
||||
valueBuf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(valueBuf)
|
||||
|
||||
// StackTrie requires values to be inserted in increasing hash order, which is not the
|
||||
// order that `list` provides hashes in. This insertion sequence ensures that the
|
||||
// order is correct.
|
||||
var indexBuf []byte
|
||||
for i := 1; i < list.Len() && i <= 0x7f; i++ {
|
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
||||
value := encodeForDerive(list, i, valueBuf)
|
||||
hasher.Update(indexBuf, value)
|
||||
}
|
||||
if list.Len() > 0 {
|
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], 0)
|
||||
value := encodeForDerive(list, 0, valueBuf)
|
||||
hasher.Update(indexBuf, value)
|
||||
}
|
||||
for i := 0x80; i < list.Len(); i++ {
|
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
|
||||
value := encodeForDerive(list, i, valueBuf)
|
||||
hasher.Update(indexBuf, value)
|
||||
}
|
||||
return hasher.Hash()
|
||||
}
|
212
core/types/hashing_test.go
Normal file
212
core/types/hashing_test.go
Normal file
@ -0,0 +1,212 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
func TestDeriveSha(t *testing.T) {
|
||||
txs, err := genTxs(0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for len(txs) < 1000 {
|
||||
exp := types.DeriveSha(txs, new(trie.Trie))
|
||||
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
|
||||
}
|
||||
newTxs, err := genTxs(uint64(len(txs) + 1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
txs = append(txs, newTxs...)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEIP2718DeriveSha tests that the input to the DeriveSha function is correct.
|
||||
func TestEIP2718DeriveSha(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
rlpData string
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
rlpData: "0xb8a701f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9",
|
||||
exp: "01 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n80 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n",
|
||||
},
|
||||
} {
|
||||
d := &hashToHumanReadable{}
|
||||
var t1, t2 types.Transaction
|
||||
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t1)
|
||||
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t2)
|
||||
txs := types.Transactions{&t1, &t2}
|
||||
types.DeriveSha(txs, d)
|
||||
if tc.exp != string(d.data) {
|
||||
t.Fatalf("Want\n%v\nhave:\n%v", tc.exp, string(d.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeriveSha200(b *testing.B) {
|
||||
txs, err := genTxs(200)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
var exp common.Hash
|
||||
var got common.Hash
|
||||
b.Run("std_trie", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
exp = types.DeriveSha(txs, new(trie.Trie))
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("stack_trie", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
got = types.DeriveSha(txs, trie.NewStackTrie(nil))
|
||||
}
|
||||
})
|
||||
if got != exp {
|
||||
b.Errorf("got %x exp %x", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzDeriveSha(t *testing.T) {
|
||||
// increase this for longer runs -- it's set to quite low for travis
|
||||
rndSeed := mrand.Int()
|
||||
for i := 0; i < 10; i++ {
|
||||
seed := rndSeed + i
|
||||
exp := types.DeriveSha(newDummy(i), new(trie.Trie))
|
||||
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
printList(newDummy(seed))
|
||||
t.Fatalf("seed %d: got %x exp %x", seed, got, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDerivableList contains testcases found via fuzzing
|
||||
func TestDerivableList(t *testing.T) {
|
||||
type tcase []string
|
||||
tcs := []tcase{
|
||||
{
|
||||
"0xc041",
|
||||
},
|
||||
{
|
||||
"0xf04cf757812428b0763112efb33b6f4fad7deb445e",
|
||||
"0xf04cf757812428b0763112efb33b6f4fad7deb445e",
|
||||
},
|
||||
{
|
||||
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d",
|
||||
"0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a",
|
||||
"0x14abd5c47c0be87b0454596baad2",
|
||||
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d",
|
||||
},
|
||||
}
|
||||
for i, tc := range tcs[1:] {
|
||||
exp := types.DeriveSha(flatList(tc), new(trie.Trie))
|
||||
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
t.Fatalf("case %d: got %x exp %x", i, got, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genTxs(num uint64) (types.Transactions, error) {
|
||||
key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
newTx := func(i uint64) (*types.Transaction, error) {
|
||||
signer := types.NewEIP155Signer(big.NewInt(18))
|
||||
utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil)
|
||||
tx, err := types.SignTx(utx, signer, key)
|
||||
return tx, err
|
||||
}
|
||||
var txs types.Transactions
|
||||
for i := uint64(0); i < num; i++ {
|
||||
tx, err := newTx(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
type dummyDerivableList struct {
|
||||
len int
|
||||
seed int
|
||||
}
|
||||
|
||||
func newDummy(seed int) *dummyDerivableList {
|
||||
d := &dummyDerivableList{}
|
||||
src := mrand.NewSource(int64(seed))
|
||||
// don't use lists longer than 4K items
|
||||
d.len = int(src.Int63() & 0x0FFF)
|
||||
d.seed = seed
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *dummyDerivableList) Len() int {
|
||||
return d.len
|
||||
}
|
||||
|
||||
func (d *dummyDerivableList) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
src := mrand.NewSource(int64(d.seed + i))
|
||||
// max item size 256, at least 1 byte per item
|
||||
size := 1 + src.Int63()&0x00FF
|
||||
io.CopyN(w, mrand.New(src), size)
|
||||
}
|
||||
|
||||
func printList(l types.DerivableList) {
|
||||
fmt.Printf("list length: %d\n", l.Len())
|
||||
fmt.Printf("{\n")
|
||||
for i := 0; i < l.Len(); i++ {
|
||||
var buf bytes.Buffer
|
||||
l.EncodeIndex(i, &buf)
|
||||
fmt.Printf("\"0x%x\",\n", buf.Bytes())
|
||||
}
|
||||
fmt.Printf("},\n")
|
||||
}
|
||||
|
||||
type flatList []string
|
||||
|
||||
func (f flatList) Len() int {
|
||||
return len(f)
|
||||
}
|
||||
func (f flatList) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
w.Write(hexutil.MustDecode(f[i]))
|
||||
}
|
||||
|
||||
type hashToHumanReadable struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (d *hashToHumanReadable) Reset() {
|
||||
d.data = make([]byte, 0)
|
||||
}
|
||||
|
||||
func (d *hashToHumanReadable) Update(i []byte, i2 []byte) {
|
||||
l := fmt.Sprintf("%x %x\n", i, i2)
|
||||
d.data = append(d.data, []byte(l)...)
|
||||
}
|
||||
|
||||
func (d *hashToHumanReadable) Hash() common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
111
core/types/legacy_tx.go
Normal file
111
core/types/legacy_tx.go
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// LegacyTx is the transaction data of regular Ethereum transactions.
|
||||
type LegacyTx struct {
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
V, R, S *big.Int // signature values
|
||||
}
|
||||
|
||||
// NewTransaction creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return NewTx(&LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &to,
|
||||
Value: amount,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// NewContractCreation creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return NewTx(&LegacyTx{
|
||||
Nonce: nonce,
|
||||
Value: amount,
|
||||
Gas: gasLimit,
|
||||
GasPrice: gasPrice,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *LegacyTx) copy() TxData {
|
||||
cpy := &LegacyTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are initialized below.
|
||||
Value: new(big.Int),
|
||||
GasPrice: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.GasPrice != nil {
|
||||
cpy.GasPrice.Set(tx.GasPrice)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *LegacyTx) txType() byte { return LegacyTxType }
|
||||
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
|
||||
func (tx *LegacyTx) accessList() AccessList { return nil }
|
||||
func (tx *LegacyTx) data() []byte { return tx.Data }
|
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value }
|
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
||||
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.V, tx.R, tx.S = v, r, s
|
||||
}
|
@ -38,6 +38,8 @@ var (
|
||||
receiptStatusSuccessfulRLP = []byte{0x01}
|
||||
)
|
||||
|
||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
||||
|
||||
const (
|
||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||
ReceiptStatusFailed = uint64(0)
|
||||
@ -49,6 +51,7 @@ const (
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields: These fields are defined by the Yellow Paper
|
||||
Type uint8 `json:"type,omitempty"`
|
||||
PostState []byte `json:"root"`
|
||||
Status uint64 `json:"status"`
|
||||
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
@ -69,6 +72,7 @@ type Receipt struct {
|
||||
}
|
||||
|
||||
type receiptMarshaling struct {
|
||||
Type hexutil.Uint64
|
||||
PostState hexutil.Bytes
|
||||
Status hexutil.Uint64
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
@ -114,8 +118,13 @@ type v3StoredReceiptRLP struct {
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
// Deprecated: create receipts using a struct literal instead.
|
||||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
|
||||
r := &Receipt{
|
||||
Type: LegacyTxType,
|
||||
PostState: common.CopyBytes(root),
|
||||
CumulativeGasUsed: cumulativeGasUsed,
|
||||
}
|
||||
if failed {
|
||||
r.Status = ReceiptStatusFailed
|
||||
} else {
|
||||
@ -127,21 +136,65 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
|
||||
// into an RLP stream. If no post state is present, byzantium fork is assumed.
|
||||
func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
if r.Type == LegacyTxType {
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
// It's an EIP-2718 typed TX receipt.
|
||||
if r.Type != AccessListTxType {
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf.WriteByte(r.Type)
|
||||
if err := rlp.Encode(buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||
// from an RLP stream.
|
||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec receiptRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
kind, _, err := s.Kind()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case kind == rlp.List:
|
||||
// It's a legacy receipt.
|
||||
var dec receiptRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(dec)
|
||||
case kind == rlp.String:
|
||||
// It's an EIP-2718 typed tx receipt.
|
||||
b, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
}
|
||||
r.Type = b[0]
|
||||
if r.Type == AccessListTxType {
|
||||
var dec receiptRLP
|
||||
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.setFromRLP(dec)
|
||||
}
|
||||
return ErrTxTypeNotSupported
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
if err := r.setStatus(dec.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Receipt) setFromRLP(data receiptRLP) error {
|
||||
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
|
||||
return r.setStatus(data.PostStateOrStatus)
|
||||
}
|
||||
|
||||
func (r *Receipt) setStatus(postStateOrStatus []byte) error {
|
||||
@ -172,7 +225,6 @@ func (r *Receipt) statusEncoding() []byte {
|
||||
// to approximate and limit the memory consumption of various caches.
|
||||
func (r *Receipt) Size() common.StorageSize {
|
||||
size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
|
||||
|
||||
size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
|
||||
for _, log := range r.Logs {
|
||||
size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
|
||||
@ -277,19 +329,27 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receipts is a wrapper around a Receipt array to implement DerivableList.
|
||||
// Receipts implements DerivableList for receipts.
|
||||
type Receipts []*Receipt
|
||||
|
||||
// Len returns the number of receipts in this list.
|
||||
func (r Receipts) Len() int { return len(r) }
|
||||
func (rs Receipts) Len() int { return len(rs) }
|
||||
|
||||
// GetRlp returns the RLP encoding of one receipt from the list.
|
||||
func (r Receipts) GetRlp(i int) []byte {
|
||||
bytes, err := rlp.EncodeToBytes(r[i])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// EncodeIndex encodes the i'th receipt to w.
|
||||
func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
r := rs[i]
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
switch r.Type {
|
||||
case LegacyTxType:
|
||||
rlp.Encode(w, data)
|
||||
case AccessListTxType:
|
||||
w.WriteByte(AccessListTxType)
|
||||
rlp.Encode(w, data)
|
||||
default:
|
||||
// For unsupported types, write nothing. Since this is for
|
||||
// DeriveSha, the error will be caught matching the derived hash
|
||||
// to the block.
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||
@ -302,7 +362,8 @@ func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, num
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
for i := 0; i < len(r); i++ {
|
||||
// The transaction hash can be retrieved from the transaction itself
|
||||
// The transaction type and hash can be retrieved from the transaction itself
|
||||
r[i].Type = txs[i].Type()
|
||||
r[i].TxHash = txs[i].Hash()
|
||||
|
||||
// block location fields
|
||||
|
@ -29,6 +29,15 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
err := rlp.DecodeBytes(input, &r)
|
||||
if err != errEmptyTypedReceipt {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyReceiptDecoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -154,9 +163,29 @@ func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
// Tests that receipt data can be correctly derived from the contextual infos
|
||||
func TestDeriveFields(t *testing.T) {
|
||||
// Create a few transactions to have receipts for
|
||||
to2 := common.HexToAddress("0x2")
|
||||
to3 := common.HexToAddress("0x3")
|
||||
txs := Transactions{
|
||||
NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil),
|
||||
NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil),
|
||||
NewTx(&LegacyTx{
|
||||
Nonce: 1,
|
||||
Value: big.NewInt(1),
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(1),
|
||||
}),
|
||||
NewTx(&LegacyTx{
|
||||
To: &to2,
|
||||
Nonce: 2,
|
||||
Value: big.NewInt(2),
|
||||
Gas: 2,
|
||||
GasPrice: big.NewInt(2),
|
||||
}),
|
||||
NewTx(&AccessListTx{
|
||||
To: &to3,
|
||||
Nonce: 3,
|
||||
Value: big.NewInt(3),
|
||||
Gas: 3,
|
||||
GasPrice: big.NewInt(3),
|
||||
}),
|
||||
}
|
||||
// Create the corresponding receipts
|
||||
receipts := Receipts{
|
||||
@ -182,6 +211,18 @@ func TestDeriveFields(t *testing.T) {
|
||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||
GasUsed: 2,
|
||||
},
|
||||
&Receipt{
|
||||
Type: AccessListTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 6,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x33})},
|
||||
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
||||
},
|
||||
TxHash: txs[2].Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||
GasUsed: 3,
|
||||
},
|
||||
}
|
||||
// Clear all the computed fields and re-derive them
|
||||
number := big.NewInt(1)
|
||||
@ -196,6 +237,9 @@ func TestDeriveFields(t *testing.T) {
|
||||
|
||||
logIndex := uint(0)
|
||||
for i := range receipts {
|
||||
if receipts[i].Type != txs[i].Type() {
|
||||
t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
|
||||
}
|
||||
if receipts[i].TxHash != txs[i].Hash() {
|
||||
t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
|
||||
}
|
||||
@ -243,6 +287,34 @@ func TestDeriveFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
||||
// rlp decoder, which failed due to a shadowing error.
|
||||
func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
||||
var payload = common.FromHex("f9043eb9010c01f90108018262d4b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010c01f901080182cd14b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f901090183013754b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b9010d01f90109018301a194b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0")
|
||||
check := func(bundle []*Receipt) {
|
||||
t.Helper()
|
||||
for i, receipt := range bundle {
|
||||
if got, want := receipt.Type, uint8(1); got != want {
|
||||
t.Fatalf("bundle %d: got %x, want %x", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var bundle []*Receipt
|
||||
rlp.DecodeBytes(payload, &bundle)
|
||||
check(bundle)
|
||||
}
|
||||
{
|
||||
var bundle []*Receipt
|
||||
r := bytes.NewReader(payload)
|
||||
s := rlp.NewStream(r, uint64(len(payload)))
|
||||
if err := s.Decode(&bundle); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
check(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
|
||||
t.Helper()
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/heap"
|
||||
"errors"
|
||||
"io"
|
||||
@ -25,20 +26,28 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
||||
|
||||
var (
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
||||
)
|
||||
|
||||
// Transaction types.
|
||||
const (
|
||||
LegacyTxType = iota
|
||||
AccessListTxType
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
type Transaction struct {
|
||||
data txdata // Consensus contents of a transaction
|
||||
time time.Time // Time first seen locally (spam avoidance)
|
||||
inner TxData // Consensus contents of a transaction
|
||||
time time.Time // Time first seen locally (spam avoidance)
|
||||
|
||||
// caches
|
||||
hash atomic.Value
|
||||
@ -46,170 +55,266 @@ type Transaction struct {
|
||||
from atomic.Value
|
||||
}
|
||||
|
||||
type txdata struct {
|
||||
AccountNonce uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *big.Int `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gas" gencodec:"required"`
|
||||
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int `json:"value" gencodec:"required"`
|
||||
Payload []byte `json:"input" gencodec:"required"`
|
||||
|
||||
// Signature values
|
||||
V *big.Int `json:"v" gencodec:"required"`
|
||||
R *big.Int `json:"r" gencodec:"required"`
|
||||
S *big.Int `json:"s" gencodec:"required"`
|
||||
|
||||
// This is only used when marshaling to JSON.
|
||||
Hash *common.Hash `json:"hash" rlp:"-"`
|
||||
// NewTx creates a new transaction.
|
||||
func NewTx(inner TxData) *Transaction {
|
||||
tx := new(Transaction)
|
||||
tx.setDecoded(inner.copy(), 0)
|
||||
return tx
|
||||
}
|
||||
|
||||
type txdataMarshaling struct {
|
||||
AccountNonce hexutil.Uint64
|
||||
Price *hexutil.Big
|
||||
GasLimit hexutil.Uint64
|
||||
Amount *hexutil.Big
|
||||
Payload hexutil.Bytes
|
||||
V *hexutil.Big
|
||||
R *hexutil.Big
|
||||
S *hexutil.Big
|
||||
// TxData is the underlying data of a transaction.
|
||||
//
|
||||
// This is implemented by LegacyTx and AccessListTx.
|
||||
type TxData interface {
|
||||
txType() byte // returns the type ID
|
||||
copy() TxData // creates a deep copy and initializes all fields
|
||||
|
||||
chainID() *big.Int
|
||||
accessList() AccessList
|
||||
data() []byte
|
||||
gas() uint64
|
||||
gasPrice() *big.Int
|
||||
value() *big.Int
|
||||
nonce() uint64
|
||||
to() *common.Address
|
||||
|
||||
rawSignatureValues() (v, r, s *big.Int)
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.Encode(w, tx.inner)
|
||||
}
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
if err := tx.encodeTyped(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
|
||||
// encodeTyped writes the canonical encoding of a typed transaction to w.
|
||||
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
|
||||
w.WriteByte(tx.Type())
|
||||
return rlp.Encode(w, tx.inner)
|
||||
}
|
||||
|
||||
func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
|
||||
if len(data) > 0 {
|
||||
data = common.CopyBytes(data)
|
||||
// MarshalBinary returns the canonical encoding of the transaction.
|
||||
// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
|
||||
// transactions, it returns the type and payload.
|
||||
func (tx *Transaction) MarshalBinary() ([]byte, error) {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.EncodeToBytes(tx.inner)
|
||||
}
|
||||
d := txdata{
|
||||
AccountNonce: nonce,
|
||||
Recipient: to,
|
||||
Payload: data,
|
||||
Amount: new(big.Int),
|
||||
GasLimit: gasLimit,
|
||||
Price: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
if amount != nil {
|
||||
d.Amount.Set(amount)
|
||||
}
|
||||
if gasPrice != nil {
|
||||
d.Price.Set(gasPrice)
|
||||
}
|
||||
return &Transaction{
|
||||
data: d,
|
||||
time: time.Now(),
|
||||
var buf bytes.Buffer
|
||||
err := tx.encodeTyped(&buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
kind, size, err := s.Kind()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case kind == rlp.List:
|
||||
// It's a legacy transaction.
|
||||
var inner LegacyTx
|
||||
err := s.Decode(&inner)
|
||||
if err == nil {
|
||||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.String:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
return err
|
||||
}
|
||||
inner, err := tx.decodeTyped(b)
|
||||
if err == nil {
|
||||
tx.setDecoded(inner, len(b))
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
}
|
||||
|
||||
// ChainId returns which chain id this transaction was signed for (if at all)
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return deriveChainId(tx.data.V)
|
||||
// UnmarshalBinary decodes the canonical encoding of transactions.
|
||||
// It supports legacy RLP transactions and EIP2718 typed transactions.
|
||||
func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
||||
if len(b) > 0 && b[0] > 0x7f {
|
||||
// It's a legacy transaction.
|
||||
var data LegacyTx
|
||||
err := rlp.DecodeBytes(b, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.setDecoded(&data, len(b))
|
||||
return nil
|
||||
}
|
||||
// It's an EIP2718 typed transaction envelope.
|
||||
inner, err := tx.decodeTyped(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.setDecoded(inner, len(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protected returns whether the transaction is protected from replay protection.
|
||||
func (tx *Transaction) Protected() bool {
|
||||
return isProtectedV(tx.data.V)
|
||||
// decodeTyped decodes a typed transaction from the canonical format.
|
||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, errEmptyTypedTx
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
var inner AccessListTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
// setDecoded sets the inner transaction and size after decoding.
|
||||
func (tx *Transaction) setDecoded(inner TxData, size int) {
|
||||
tx.inner = inner
|
||||
tx.time = time.Now()
|
||||
if size > 0 {
|
||||
tx.size.Store(common.StorageSize(size))
|
||||
}
|
||||
}
|
||||
|
||||
func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error {
|
||||
if isProtectedV(v) && !maybeProtected {
|
||||
return ErrUnexpectedProtection
|
||||
}
|
||||
|
||||
var plainV byte
|
||||
if isProtectedV(v) {
|
||||
chainID := deriveChainId(v).Uint64()
|
||||
plainV = byte(v.Uint64() - 35 - 2*chainID)
|
||||
} else if maybeProtected {
|
||||
// Only EIP-155 signatures can be optionally protected. Since
|
||||
// we determined this v value is not protected, it must be a
|
||||
// raw 27 or 28.
|
||||
plainV = byte(v.Uint64() - 27)
|
||||
} else {
|
||||
// If the signature is not optionally protected, we assume it
|
||||
// must already be equal to the recovery id.
|
||||
plainV = byte(v.Uint64())
|
||||
}
|
||||
if !crypto.ValidateSignatureValues(plainV, r, s, false) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isProtectedV(V *big.Int) bool {
|
||||
if V.BitLen() <= 8 {
|
||||
v := V.Uint64()
|
||||
return v != 27 && v != 28
|
||||
return v != 27 && v != 28 && v != 1 && v != 0
|
||||
}
|
||||
// anything not 27 or 28 is considered protected
|
||||
return true
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &tx.data)
|
||||
// Protected says whether the transaction is replay-protected.
|
||||
func (tx *Transaction) Protected() bool {
|
||||
switch tx := tx.inner.(type) {
|
||||
case *LegacyTx:
|
||||
return tx.V != nil && isProtectedV(tx.V)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
_, size, _ := s.Kind()
|
||||
err := s.Decode(&tx.data)
|
||||
if err == nil {
|
||||
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
|
||||
tx.time = time.Now()
|
||||
}
|
||||
return err
|
||||
// Type returns the transaction type.
|
||||
func (tx *Transaction) Type() uint8 {
|
||||
return tx.inner.txType()
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
hash := tx.Hash()
|
||||
data := tx.data
|
||||
data.Hash = &hash
|
||||
return data.MarshalJSON()
|
||||
// ChainId returns the EIP155 chain ID of the transaction. The return value will always be
|
||||
// non-nil. For legacy transactions which are not replay-protected, the return value is
|
||||
// zero.
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return tx.inner.chainID()
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var dec txdata
|
||||
if err := dec.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0
|
||||
if withSignature {
|
||||
var V byte
|
||||
if isProtectedV(dec.V) {
|
||||
chainID := deriveChainId(dec.V).Uint64()
|
||||
V = byte(dec.V.Uint64() - 35 - 2*chainID)
|
||||
} else {
|
||||
V = byte(dec.V.Uint64() - 27)
|
||||
}
|
||||
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
}
|
||||
*tx = Transaction{
|
||||
data: dec,
|
||||
time: time.Now(),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Data returns the input data of the transaction.
|
||||
func (tx *Transaction) Data() []byte { return tx.inner.data() }
|
||||
|
||||
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
||||
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
|
||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
|
||||
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
||||
return tx.data.Price.Cmp(other.data.Price)
|
||||
}
|
||||
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
||||
return tx.data.Price.Cmp(other)
|
||||
}
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
|
||||
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
|
||||
func (tx *Transaction) CheckNonce() bool { return true }
|
||||
// AccessList returns the access list of the transaction.
|
||||
func (tx *Transaction) AccessList() AccessList { return tx.inner.accessList() }
|
||||
|
||||
// Gas returns the gas limit of the transaction.
|
||||
func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
|
||||
|
||||
// GasPrice returns the gas price of the transaction.
|
||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
||||
|
||||
// Value returns the ether amount of the transaction.
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
||||
|
||||
// Nonce returns the sender account nonce of the transaction.
|
||||
func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
||||
|
||||
// To returns the recipient address of the transaction.
|
||||
// It returns nil if the transaction is a contract creation.
|
||||
// For contract-creation transactions, To returns nil.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
if tx.data.Recipient == nil {
|
||||
// Copy the pointed-to address.
|
||||
ito := tx.inner.to()
|
||||
if ito == nil {
|
||||
return nil
|
||||
}
|
||||
to := *tx.data.Recipient
|
||||
return &to
|
||||
cpy := *ito
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Hash hashes the RLP encoding of tx.
|
||||
// It uniquely identifies the transaction.
|
||||
// Cost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
|
||||
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||
// The return values should not be modified by the caller.
|
||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.inner.rawSignatureValues()
|
||||
}
|
||||
|
||||
// GasPriceCmp compares the gas prices of two transactions.
|
||||
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
||||
return tx.inner.gasPrice().Cmp(other.GasPrice())
|
||||
}
|
||||
|
||||
// GasPriceIntCmp compares the gas price of the transaction against the given price.
|
||||
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
||||
return tx.inner.gasPrice().Cmp(other)
|
||||
}
|
||||
|
||||
// Hash returns the transaction hash.
|
||||
func (tx *Transaction) Hash() common.Hash {
|
||||
if hash := tx.hash.Load(); hash != nil {
|
||||
return hash.(common.Hash)
|
||||
}
|
||||
v := rlpHash(tx)
|
||||
tx.hash.Store(v)
|
||||
return v
|
||||
|
||||
var h common.Hash
|
||||
if tx.Type() == LegacyTxType {
|
||||
h = rlpHash(tx.inner)
|
||||
} else {
|
||||
h = prefixedRlpHash(tx.Type(), tx.inner)
|
||||
}
|
||||
tx.hash.Store(h)
|
||||
return h
|
||||
}
|
||||
|
||||
// Size returns the true RLP encoded storage size of the transaction, either by
|
||||
@ -219,32 +324,11 @@ func (tx *Transaction) Size() common.StorageSize {
|
||||
return size.(common.StorageSize)
|
||||
}
|
||||
c := writeCounter(0)
|
||||
rlp.Encode(&c, &tx.data)
|
||||
rlp.Encode(&c, &tx.inner)
|
||||
tx.size.Store(common.StorageSize(c))
|
||||
return common.StorageSize(c)
|
||||
}
|
||||
|
||||
// AsMessage returns the transaction as a core.Message.
|
||||
//
|
||||
// AsMessage requires a signer to derive the sender.
|
||||
//
|
||||
// XXX Rename message to something less arbitrary?
|
||||
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
||||
msg := Message{
|
||||
nonce: tx.data.AccountNonce,
|
||||
gasLimit: tx.data.GasLimit,
|
||||
gasPrice: new(big.Int).Set(tx.data.Price),
|
||||
to: tx.data.Recipient,
|
||||
amount: tx.data.Amount,
|
||||
data: tx.data.Payload,
|
||||
checkNonce: true,
|
||||
}
|
||||
|
||||
var err error
|
||||
msg.from, err = Sender(s, tx)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// WithSignature returns a new transaction with the given signature.
|
||||
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
||||
@ -252,40 +336,27 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpy := &Transaction{
|
||||
data: tx.data,
|
||||
time: tx.time,
|
||||
}
|
||||
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
|
||||
return cpy, nil
|
||||
cpy := tx.inner.copy()
|
||||
cpy.setSignatureValues(signer.ChainID(), v, r, s)
|
||||
return &Transaction{inner: cpy, time: tx.time}, nil
|
||||
}
|
||||
|
||||
// Cost returns amount + gasprice * gaslimit.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
|
||||
total.Add(total, tx.data.Amount)
|
||||
return total
|
||||
}
|
||||
|
||||
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||
// The return values should not be modified by the caller.
|
||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.data.V, tx.data.R, tx.data.S
|
||||
}
|
||||
|
||||
// Transactions is a Transaction slice type for basic sorting.
|
||||
// Transactions implements DerivableList for transactions.
|
||||
type Transactions []*Transaction
|
||||
|
||||
// Len returns the length of s.
|
||||
func (s Transactions) Len() int { return len(s) }
|
||||
|
||||
// Swap swaps the i'th and the j'th element in s.
|
||||
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// GetRlp implements Rlpable and returns the i'th element of s in rlp.
|
||||
func (s Transactions) GetRlp(i int) []byte {
|
||||
enc, _ := rlp.EncodeToBytes(s[i])
|
||||
return enc
|
||||
// EncodeIndex encodes the i'th transaction to w. Note that this does not check for errors
|
||||
// because we assume that *Transaction will only ever contain valid txs that were either
|
||||
// constructed by decoding or via public API in this package.
|
||||
func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
tx := s[i]
|
||||
if tx.Type() == LegacyTxType {
|
||||
rlp.Encode(w, tx.inner)
|
||||
} else {
|
||||
tx.encodeTyped(w)
|
||||
}
|
||||
}
|
||||
|
||||
// TxDifference returns a new set which is the difference between a and b.
|
||||
@ -312,7 +383,7 @@ func TxDifference(a, b Transactions) Transactions {
|
||||
type TxByNonce Transactions
|
||||
|
||||
func (s TxByNonce) Len() int { return len(s) }
|
||||
func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
|
||||
func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() }
|
||||
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
|
||||
@ -323,7 +394,7 @@ func (s TxByPriceAndTime) Len() int { return len(s) }
|
||||
func (s TxByPriceAndTime) Less(i, j int) bool {
|
||||
// If the prices are equal, use the time the transaction was first seen for
|
||||
// deterministic sorting
|
||||
cmp := s[i].data.Price.Cmp(s[j].data.Price)
|
||||
cmp := s[i].GasPrice().Cmp(s[j].GasPrice())
|
||||
if cmp == 0 {
|
||||
return s[i].time.Before(s[j].time)
|
||||
}
|
||||
@ -361,13 +432,13 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
|
||||
// Initialize a price and received time based heap with the head transactions
|
||||
heads := make(TxByPriceAndTime, 0, len(txs))
|
||||
for from, accTxs := range txs {
|
||||
heads = append(heads, accTxs[0])
|
||||
// Ensure the sender address is from the signer
|
||||
acc, _ := Sender(signer, accTxs[0])
|
||||
txs[acc] = accTxs[1:]
|
||||
if from != acc {
|
||||
if acc, _ := Sender(signer, accTxs[0]); acc != from {
|
||||
delete(txs, from)
|
||||
continue
|
||||
}
|
||||
heads = append(heads, accTxs[0])
|
||||
txs[from] = accTxs[1:]
|
||||
}
|
||||
heap.Init(&heads)
|
||||
|
||||
@ -416,10 +487,11 @@ type Message struct {
|
||||
gasLimit uint64
|
||||
gasPrice *big.Int
|
||||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
}
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
|
||||
return Message{
|
||||
from: from,
|
||||
to: to,
|
||||
@ -428,15 +500,35 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
||||
gasLimit: gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
}
|
||||
}
|
||||
|
||||
func (m Message) From() common.Address { return m.from }
|
||||
func (m Message) To() *common.Address { return m.to }
|
||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
||||
func (m Message) Value() *big.Int { return m.amount }
|
||||
func (m Message) Gas() uint64 { return m.gasLimit }
|
||||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
// AsMessage returns the transaction as a core.Message.
|
||||
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
||||
msg := Message{
|
||||
nonce: tx.Nonce(),
|
||||
gasLimit: tx.Gas(),
|
||||
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
||||
to: tx.To(),
|
||||
amount: tx.Value(),
|
||||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
}
|
||||
|
||||
var err error
|
||||
msg.from, err = Sender(s, tx)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func (m Message) From() common.Address { return m.from }
|
||||
func (m Message) To() *common.Address { return m.to }
|
||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
||||
func (m Message) Value() *big.Int { return m.amount }
|
||||
func (m Message) Gas() uint64 { return m.gasLimit }
|
||||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
|
187
core/types/transaction_marshalling.go
Normal file
187
core/types/transaction_marshalling.go
Normal file
@ -0,0 +1,187 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// txJSON is the JSON representation of transactions.
|
||||
type txJSON struct {
|
||||
Type hexutil.Uint64 `json:"type"`
|
||||
|
||||
// Common transaction fields:
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
To *common.Address `json:"to"`
|
||||
|
||||
// Access list transaction fields:
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
AccessList *AccessList `json:"accessList,omitempty"`
|
||||
|
||||
// Only used for encoding:
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
// MarshalJSON marshals as JSON with a hash.
|
||||
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||
var enc txJSON
|
||||
// These are set for all tx types.
|
||||
enc.Hash = t.Hash()
|
||||
enc.Type = hexutil.Uint64(t.Type())
|
||||
|
||||
// Other fields are set conditionally depending on tx type.
|
||||
switch tx := t.inner.(type) {
|
||||
case *LegacyTx:
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
case *AccessListTx:
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainID)
|
||||
enc.AccessList = &tx.AccessList
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var dec txJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode / verify fields according to transaction type.
|
||||
var inner TxData
|
||||
switch dec.Type {
|
||||
case LegacyTxType:
|
||||
var itx LegacyTx
|
||||
inner = &itx
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' in transaction")
|
||||
}
|
||||
itx.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in transaction")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case AccessListTxType:
|
||||
var itx AccessListTx
|
||||
inner = &itx
|
||||
// Access list is optional for now.
|
||||
if dec.AccessList != nil {
|
||||
itx.AccessList = *dec.AccessList
|
||||
}
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
itx.ChainID = (*big.Int)(dec.ChainID)
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' in transaction")
|
||||
}
|
||||
itx.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in transaction")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
|
||||
// Now set the inner transaction.
|
||||
t.setDecoded(inner, 0)
|
||||
|
||||
// TODO: check hash here?
|
||||
return nil
|
||||
}
|
@ -27,9 +27,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||
)
|
||||
var ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||
|
||||
// sigCache is used to cache the derived sender and contains
|
||||
// the signer used to derive it.
|
||||
@ -42,6 +40,8 @@ type sigCache struct {
|
||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case config.IsYoloV3(blockNumber):
|
||||
signer = NewEIP2930Signer(config.ChainID)
|
||||
case config.IsEIP155(blockNumber):
|
||||
signer = NewEIP155Signer(config.ChainID)
|
||||
case config.IsHomestead(blockNumber):
|
||||
@ -52,7 +52,40 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
return signer
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key
|
||||
// LatestSigner returns the 'most permissive' Signer available for the given chain
|
||||
// configuration. Specifically, this enables support of EIP-155 replay protection and
|
||||
// EIP-2930 access list transactions when their respective forks are scheduled to occur at
|
||||
// any block number in the chain config.
|
||||
//
|
||||
// Use this in transaction-handling code where the current block number is unknown. If you
|
||||
// have the current block number available, use MakeSigner instead.
|
||||
func LatestSigner(config *params.ChainConfig) Signer {
|
||||
if config.ChainID != nil {
|
||||
if config.YoloV3Block != nil {
|
||||
return NewEIP2930Signer(config.ChainID)
|
||||
}
|
||||
if config.EIP155Block != nil {
|
||||
return NewEIP155Signer(config.ChainID)
|
||||
}
|
||||
}
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
|
||||
// LatestSignerForChainID returns the 'most permissive' Signer available. Specifically,
|
||||
// this enables support for EIP-155 replay protection and all implemented EIP-2718
|
||||
// transaction types if chainID is non-nil.
|
||||
//
|
||||
// Use this in transaction-handling code where the current block number and fork
|
||||
// configuration are unknown. If you have a ChainConfig, use LatestSigner instead.
|
||||
// If you have a ChainConfig and know the current block number, use MakeSigner instead.
|
||||
func LatestSignerForChainID(chainID *big.Int) Signer {
|
||||
if chainID == nil {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
return NewEIP2930Signer(chainID)
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key.
|
||||
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
@ -62,6 +95,27 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
|
||||
return tx.WithSignature(s, sig)
|
||||
}
|
||||
|
||||
// SignNewTx creates a transaction and signs it.
|
||||
func SignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) (*Transaction, error) {
|
||||
tx := NewTx(txdata)
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.WithSignature(s, sig)
|
||||
}
|
||||
|
||||
// MustSignNewTx creates a transaction and signs it.
|
||||
// This panics if the transaction cannot be signed.
|
||||
func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction {
|
||||
tx, err := SignNewTx(prv, s, txdata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
||||
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||
// signature.
|
||||
@ -88,21 +142,128 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// Signer encapsulates transaction signature handling. Note that this interface is not a
|
||||
// stable API and may change at any time to accommodate new protocol rules.
|
||||
// Signer encapsulates transaction signature handling. The name of this type is slightly
|
||||
// misleading because Signers don't actually sign, they're just for validating and
|
||||
// processing of signatures.
|
||||
//
|
||||
// Note that this interface is not a stable API and may change at any time to accommodate
|
||||
// new protocol rules.
|
||||
type Signer interface {
|
||||
// Sender returns the sender address of the transaction.
|
||||
Sender(tx *Transaction) (common.Address, error)
|
||||
|
||||
// SignatureValues returns the raw R, S, V values corresponding to the
|
||||
// given signature.
|
||||
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
|
||||
// Hash returns the hash to be signed.
|
||||
ChainID() *big.Int
|
||||
|
||||
// Hash returns 'signature hash', i.e. the transaction hash that is signed by the
|
||||
// private key. This hash does not uniquely identify the transaction.
|
||||
Hash(tx *Transaction) common.Hash
|
||||
|
||||
// Equal returns true if the given signer is the same as the receiver.
|
||||
Equal(Signer) bool
|
||||
}
|
||||
|
||||
// EIP155Transaction implements Signer using the EIP155 rules.
|
||||
type eip2930Signer struct{ EIP155Signer }
|
||||
|
||||
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
|
||||
// EIP-155 replay protected transactions, and legacy Homestead transactions.
|
||||
func NewEIP2930Signer(chainId *big.Int) Signer {
|
||||
return eip2930Signer{NewEIP155Signer(chainId)}
|
||||
}
|
||||
|
||||
func (s eip2930Signer) ChainID() *big.Int {
|
||||
return s.chainId
|
||||
}
|
||||
|
||||
func (s eip2930Signer) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(eip2930Signer)
|
||||
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||
}
|
||||
|
||||
func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
switch tx.Type() {
|
||||
case LegacyTxType:
|
||||
if !tx.Protected() {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
}
|
||||
V = new(big.Int).Sub(V, s.chainIdMul)
|
||||
V.Sub(V, big8)
|
||||
case AccessListTxType:
|
||||
// ACL txs are defined to use 0 and 1 as their recovery id, add
|
||||
// 27 to become equivalent to unprotected Homestead signatures.
|
||||
V = new(big.Int).Add(V, big.NewInt(27))
|
||||
default:
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, ErrInvalidChainId
|
||||
}
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
switch txdata := tx.inner.(type) {
|
||||
case *LegacyTx:
|
||||
R, S, V = decodeSignature(sig)
|
||||
if s.chainId.Sign() != 0 {
|
||||
V = big.NewInt(int64(sig[64] + 35))
|
||||
V.Add(V, s.chainIdMul)
|
||||
}
|
||||
case *AccessListTx:
|
||||
// Check that chain ID of tx matches the signer. We also accept ID zero here,
|
||||
// because it indicates that the chain ID was not specified in the tx.
|
||||
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
|
||||
return nil, nil, nil, ErrInvalidChainId
|
||||
}
|
||||
R, S, _ = decodeSignature(sig)
|
||||
V = big.NewInt(int64(sig[64]))
|
||||
default:
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
return R, S, V, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
|
||||
switch tx.Type() {
|
||||
case LegacyTxType:
|
||||
return rlpHash([]interface{}{
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
s.chainId, uint(0), uint(0),
|
||||
})
|
||||
case AccessListTxType:
|
||||
return prefixedRlpHash(
|
||||
tx.Type(),
|
||||
[]interface{}{
|
||||
s.chainId,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
tx.AccessList(),
|
||||
})
|
||||
default:
|
||||
// This _should_ not happen, but in case someone sends in a bad
|
||||
// json struct via RPC, it's probably more prudent to return an
|
||||
// empty hash instead of killing the node with a panic
|
||||
//panic("Unsupported transaction type: %d", tx.typ)
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
|
||||
// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
|
||||
// are replay-protected as well as unprotected homestead transactions.
|
||||
type EIP155Signer struct {
|
||||
chainId, chainIdMul *big.Int
|
||||
}
|
||||
@ -117,6 +278,10 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer {
|
||||
}
|
||||
}
|
||||
|
||||
func (s EIP155Signer) ChainID() *big.Int {
|
||||
return s.chainId
|
||||
}
|
||||
|
||||
func (s EIP155Signer) Equal(s2 Signer) bool {
|
||||
eip155, ok := s2.(EIP155Signer)
|
||||
return ok && eip155.chainId.Cmp(s.chainId) == 0
|
||||
@ -125,24 +290,28 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
|
||||
var big8 = big.NewInt(8)
|
||||
|
||||
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
if !tx.Protected() {
|
||||
return HomesteadSigner{}.Sender(tx)
|
||||
}
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, ErrInvalidChainId
|
||||
}
|
||||
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
V = new(big.Int).Sub(V, s.chainIdMul)
|
||||
V.Sub(V, big8)
|
||||
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
if tx.Type() != LegacyTxType {
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
R, S, V = decodeSignature(sig)
|
||||
if s.chainId.Sign() != 0 {
|
||||
V = big.NewInt(int64(sig[64] + 35))
|
||||
V.Add(V, s.chainIdMul)
|
||||
@ -154,12 +323,12 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
s.chainId, uint(0), uint(0),
|
||||
})
|
||||
}
|
||||
@ -168,6 +337,10 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
|
||||
// homestead rules.
|
||||
type HomesteadSigner struct{ FrontierSigner }
|
||||
|
||||
func (s HomesteadSigner) ChainID() *big.Int {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s HomesteadSigner) Equal(s2 Signer) bool {
|
||||
_, ok := s2.(HomesteadSigner)
|
||||
return ok
|
||||
@ -180,25 +353,39 @@ func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v
|
||||
}
|
||||
|
||||
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
return recoverPlain(hs.Hash(tx), r, s, v, true)
|
||||
}
|
||||
|
||||
type FrontierSigner struct{}
|
||||
|
||||
func (s FrontierSigner) ChainID() *big.Int {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s FrontierSigner) Equal(s2 Signer) bool {
|
||||
_, ok := s2.(FrontierSigner)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != LegacyTxType {
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
}
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
return recoverPlain(fs.Hash(tx), r, s, v, false)
|
||||
}
|
||||
|
||||
// SignatureValues returns signature values. This signature
|
||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
|
||||
if len(sig) != crypto.SignatureLength {
|
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
|
||||
if tx.Type() != LegacyTxType {
|
||||
return nil, nil, nil, ErrTxTypeNotSupported
|
||||
}
|
||||
r = new(big.Int).SetBytes(sig[:32])
|
||||
s = new(big.Int).SetBytes(sig[32:64])
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
r, s, v = decodeSignature(sig)
|
||||
return r, s, v, nil
|
||||
}
|
||||
|
||||
@ -206,17 +393,23 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
|
||||
// It does not uniquely identify the transaction.
|
||||
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
tx.Nonce(),
|
||||
tx.GasPrice(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
})
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
|
||||
func decodeSignature(sig []byte) (r, s, v *big.Int) {
|
||||
if len(sig) != crypto.SignatureLength {
|
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
|
||||
}
|
||||
r = new(big.Int).SetBytes(sig[:32])
|
||||
s = new(big.Int).SetBytes(sig[32:64])
|
||||
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
return r, s, v
|
||||
}
|
||||
|
||||
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
|
||||
|
@ -20,7 +20,9 @@ import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -32,6 +34,8 @@ import (
|
||||
// The values in those tests are from the Transaction Tests
|
||||
// at github.com/ethereum/tests.
|
||||
var (
|
||||
testAddr = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
||||
|
||||
emptyTx = NewTransaction(
|
||||
0,
|
||||
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
|
||||
@ -41,7 +45,7 @@ var (
|
||||
|
||||
rightvrsTx, _ = NewTransaction(
|
||||
3,
|
||||
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
|
||||
testAddr,
|
||||
big.NewInt(10),
|
||||
2000,
|
||||
big.NewInt(1),
|
||||
@ -50,8 +54,32 @@ var (
|
||||
HomesteadSigner{},
|
||||
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
|
||||
)
|
||||
|
||||
emptyEip2718Tx = NewTx(&AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 3,
|
||||
To: &testAddr,
|
||||
Value: big.NewInt(10),
|
||||
Gas: 25000,
|
||||
GasPrice: big.NewInt(1),
|
||||
Data: common.FromHex("5544"),
|
||||
})
|
||||
|
||||
signedEip2718Tx, _ = emptyEip2718Tx.WithSignature(
|
||||
NewEIP2930Signer(big.NewInt(1)),
|
||||
common.Hex2Bytes("c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101"),
|
||||
)
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedTx(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var tx Transaction
|
||||
err := rlp.DecodeBytes(input, &tx)
|
||||
if err != errEmptyTypedTx {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransactionSigHash(t *testing.T) {
|
||||
var homestead HomesteadSigner
|
||||
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
||||
@ -73,10 +101,121 @@ func TestTransactionEncode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP2718TransactionSigHash(t *testing.T) {
|
||||
s := NewEIP2930Signer(big.NewInt(1))
|
||||
if s.Hash(emptyEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
|
||||
t.Errorf("empty EIP-2718 transaction hash mismatch, got %x", s.Hash(emptyEip2718Tx))
|
||||
}
|
||||
if s.Hash(signedEip2718Tx) != common.HexToHash("49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3") {
|
||||
t.Errorf("signed EIP-2718 transaction hash mismatch, got %x", s.Hash(signedEip2718Tx))
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks signature operations on access list transactions.
|
||||
func TestEIP2930Signer(t *testing.T) {
|
||||
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
keyAddr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
signer1 = NewEIP2930Signer(big.NewInt(1))
|
||||
signer2 = NewEIP2930Signer(big.NewInt(2))
|
||||
tx0 = NewTx(&AccessListTx{Nonce: 1})
|
||||
tx1 = NewTx(&AccessListTx{ChainID: big.NewInt(1), Nonce: 1})
|
||||
tx2, _ = SignNewTx(key, signer2, &AccessListTx{ChainID: big.NewInt(2), Nonce: 1})
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
tx *Transaction
|
||||
signer Signer
|
||||
wantSignerHash common.Hash
|
||||
wantSenderErr error
|
||||
wantSignErr error
|
||||
wantHash common.Hash // after signing
|
||||
}{
|
||||
{
|
||||
tx: tx0,
|
||||
signer: signer1,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
|
||||
},
|
||||
{
|
||||
tx: tx1,
|
||||
signer: signer1,
|
||||
wantSenderErr: ErrInvalidSig,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"),
|
||||
},
|
||||
{
|
||||
// This checks what happens when trying to sign an unsigned tx for the wrong chain.
|
||||
tx: tx1,
|
||||
signer: signer2,
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantSignerHash: common.HexToHash("367967247499343401261d718ed5aa4c9486583e4d89251afce47f4a33c33362"),
|
||||
wantSignErr: ErrInvalidChainId,
|
||||
},
|
||||
{
|
||||
// This checks what happens when trying to re-sign a signed tx for the wrong chain.
|
||||
tx: tx2,
|
||||
signer: signer1,
|
||||
wantSenderErr: ErrInvalidChainId,
|
||||
wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"),
|
||||
wantSignErr: ErrInvalidChainId,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
sigHash := test.signer.Hash(test.tx)
|
||||
if sigHash != test.wantSignerHash {
|
||||
t.Errorf("test %d: wrong sig hash: got %x, want %x", i, sigHash, test.wantSignerHash)
|
||||
}
|
||||
sender, err := Sender(test.signer, test.tx)
|
||||
if err != test.wantSenderErr {
|
||||
t.Errorf("test %d: wrong Sender error %q", i, err)
|
||||
}
|
||||
if err == nil && sender != keyAddr {
|
||||
t.Errorf("test %d: wrong sender address %x", i, sender)
|
||||
}
|
||||
signedTx, err := SignTx(test.tx, test.signer, key)
|
||||
if err != test.wantSignErr {
|
||||
t.Fatalf("test %d: wrong SignTx error %q", i, err)
|
||||
}
|
||||
if signedTx != nil {
|
||||
if signedTx.Hash() != test.wantHash {
|
||||
t.Errorf("test %d: wrong tx hash after signing: got %x, want %x", i, signedTx.Hash(), test.wantHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP2718TransactionEncode(t *testing.T) {
|
||||
// RLP representation
|
||||
{
|
||||
have, err := rlp.EncodeToBytes(signedEip2718Tx)
|
||||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
want := common.FromHex("b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
|
||||
if !bytes.Equal(have, want) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", have)
|
||||
}
|
||||
}
|
||||
// Binary representation
|
||||
{
|
||||
have, err := signedEip2718Tx.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
want := common.FromHex("01f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521")
|
||||
if !bytes.Equal(have, want) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeTx(data []byte) (*Transaction, error) {
|
||||
var tx Transaction
|
||||
t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
|
||||
|
||||
return t, err
|
||||
}
|
||||
|
||||
@ -219,50 +358,125 @@ func TestTransactionTimeSort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransactionJSON tests serializing/de-serializing to/from JSON.
|
||||
func TestTransactionJSON(t *testing.T) {
|
||||
// TestTransactionCoding tests serializing/de-serializing to/from rlp and JSON.
|
||||
func TestTransactionCoding(t *testing.T) {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate key: %v", err)
|
||||
}
|
||||
signer := NewEIP155Signer(common.Big1)
|
||||
|
||||
transactions := make([]*Transaction, 0, 50)
|
||||
for i := uint64(0); i < 25; i++ {
|
||||
var tx *Transaction
|
||||
switch i % 2 {
|
||||
var (
|
||||
signer = NewEIP2930Signer(common.Big1)
|
||||
addr = common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
recipient = common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
|
||||
accesses = AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}}
|
||||
)
|
||||
for i := uint64(0); i < 500; i++ {
|
||||
var txdata TxData
|
||||
switch i % 5 {
|
||||
case 0:
|
||||
tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef"))
|
||||
// Legacy tx.
|
||||
txdata = &LegacyTx{
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(2),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 1:
|
||||
tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"))
|
||||
// Legacy tx contract creation.
|
||||
txdata = &LegacyTx{
|
||||
Nonce: i,
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(2),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 2:
|
||||
// Tx with non-zero access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: accesses,
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 3:
|
||||
// Tx with empty access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
To: &recipient,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
Data: []byte("abcdef"),
|
||||
}
|
||||
case 4:
|
||||
// Contract creation with access list.
|
||||
txdata = &AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: i,
|
||||
Gas: 123457,
|
||||
GasPrice: big.NewInt(10),
|
||||
AccessList: accesses,
|
||||
}
|
||||
}
|
||||
transactions = append(transactions, tx)
|
||||
|
||||
signedTx, err := SignTx(tx, signer, key)
|
||||
tx, err := SignNewTx(key, signer, txdata)
|
||||
if err != nil {
|
||||
t.Fatalf("could not sign transaction: %v", err)
|
||||
}
|
||||
|
||||
transactions = append(transactions, signedTx)
|
||||
}
|
||||
|
||||
for _, tx := range transactions {
|
||||
data, err := json.Marshal(tx)
|
||||
// RLP
|
||||
parsedTx, err := encodeDecodeBinary(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal failed: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
|
||||
var parsedTx *Transaction
|
||||
if err := json.Unmarshal(data, &parsedTx); err != nil {
|
||||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||||
}
|
||||
|
||||
// compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
|
||||
if tx.Hash() != parsedTx.Hash() {
|
||||
t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx)
|
||||
}
|
||||
if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 {
|
||||
t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId())
|
||||
// JSON
|
||||
parsedTx, err = encodeDecodeJSON(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
|
||||
data, err := json.Marshal(tx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
if err := json.Unmarshal(data, &parsedTx); err != nil {
|
||||
return nil, fmt.Errorf("json decoding failed: %v", err)
|
||||
}
|
||||
return parsedTx, nil
|
||||
}
|
||||
|
||||
func encodeDecodeBinary(tx *Transaction) (*Transaction, error) {
|
||||
data, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rlp encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
if err := parsedTx.UnmarshalBinary(data); err != nil {
|
||||
return nil, fmt.Errorf("rlp decoding failed: %v", err)
|
||||
}
|
||||
return parsedTx, nil
|
||||
}
|
||||
|
||||
func assertEqual(orig *Transaction, cpy *Transaction) error {
|
||||
// compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
|
||||
if want, got := orig.Hash(), cpy.Hash(); want != got {
|
||||
return fmt.Errorf("parsed tx differs from original tx, want %v, got %v", want, got)
|
||||
}
|
||||
if want, got := orig.ChainId(), cpy.ChainId(); want.Cmp(got) != 0 {
|
||||
return fmt.Errorf("invalid chain id, want %d, got %d", want, got)
|
||||
}
|
||||
if orig.AccessList() != nil {
|
||||
if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) {
|
||||
return fmt.Errorf("access list wrong!")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ type StateDB interface {
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
|
||||
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||
AddressInAccessList(addr common.Address) bool
|
||||
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
|
||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||
|
@ -114,11 +114,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
)
|
||||
if cfg.ChainConfig.IsYoloV3(vmenv.Context.BlockNumber) {
|
||||
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||
cfg.State.AddAddressToAccessList(address)
|
||||
for _, addr := range vmenv.ActivePrecompiles() {
|
||||
cfg.State.AddAddressToAccessList(addr)
|
||||
}
|
||||
cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
|
||||
}
|
||||
cfg.State.CreateAccount(address)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
@ -150,10 +146,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
)
|
||||
if cfg.ChainConfig.IsYoloV3(vmenv.Context.BlockNumber) {
|
||||
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||
for _, addr := range vmenv.ActivePrecompiles() {
|
||||
cfg.State.AddAddressToAccessList(addr)
|
||||
}
|
||||
cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil)
|
||||
}
|
||||
|
||||
// Call the code with the given configuration.
|
||||
@ -177,12 +170,9 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
||||
vmenv := NewEnv(cfg)
|
||||
|
||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
statedb := cfg.State
|
||||
if cfg.ChainConfig.IsYoloV3(vmenv.Context.BlockNumber) {
|
||||
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||
cfg.State.AddAddressToAccessList(address)
|
||||
for _, addr := range vmenv.ActivePrecompiles() {
|
||||
cfg.State.AddAddressToAccessList(addr)
|
||||
}
|
||||
statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
|
||||
}
|
||||
|
||||
// Call the code with the given configuration.
|
||||
|
Reference in New Issue
Block a user