From 67040263ec9b34e01758cfdede8a66852ddfa0bf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 19 Nov 2025 12:39:25 +0800 Subject: [PATCH 01/10] core, eth: for types with accurate size calcs, return uint64 #26046 --- core/blockchain.go | 2 +- core/txpool/txpool.go | 2 +- core/types/block.go | 12 ++-- core/types/block_test.go | 4 +- core/types/transaction.go | 34 +++++++---- core/types/transaction_marshalling.go | 82 ++++++++++++++------------- core/types/transaction_test.go | 68 ++++++++++++++++++++++ eth/downloader/queue.go | 2 +- eth/sync.go | 2 +- 9 files changed, 144 insertions(+), 64 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 16ed45e5265a..5aa6645de60c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2046,7 +2046,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i // Import all the pruned blocks to make the state available var ( blocks []*types.Block - memory common.StorageSize + memory uint64 ) for i := len(hashes) - 1; i >= 0; i-- { // Append the next block to our batch diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index c2e2aa3f4c4a..4880e881553b 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -627,7 +627,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return core.ErrTxTypeNotSupported } // Reject transactions over defined size to prevent DOS attacks - if uint64(tx.Size()) > txMaxSize { + if tx.Size() > txMaxSize { return ErrOversizedData } // Get current block number diff --git a/core/types/block.go b/core/types/block.go index 24472a2830d0..0ae843948598 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -325,7 +325,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { return err } b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs - b.size.Store(common.StorageSize(rlp.ListSize(size))) + b.size.Store(rlp.ListSize(size)) return nil } @@ -412,17 +412,17 @@ func (b *Block) HashNoValidator() common.Hash { // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previsouly cached value. -func (b *Block) Size() common.StorageSize { +func (b *Block) Size() uint64 { if size := b.size.Load(); size != nil { - return size.(common.StorageSize) + return size.(uint64) } c := writeCounter(0) rlp.Encode(&c, b) - b.size.Store(common.StorageSize(c)) - return common.StorageSize(c) + b.size.Store(uint64(c)) + return uint64(c) } -type writeCounter common.StorageSize +type writeCounter uint64 func (c *writeCounter) Write(b []byte) (int, error) { *c += writeCounter(len(b)) diff --git a/core/types/block_test.go b/core/types/block_test.go index 1b7587d3fdfb..db882e74e91e 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -53,7 +53,7 @@ func TestBlockEncoding(t *testing.T) { check("Hash", block.Hash().String(), common.HexToHash("e8d9d473fdeddd3079988fa7be58f582b7b2800e90917d4bb6f11155ce4dba30").String()) check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) check("Time", block.Time(), uint64(1426516743)) - check("Size", block.Size(), common.StorageSize(len(blockEnc))) + check("Size", block.Size(), uint64(len(blockEnc))) ourBlockEnc, err := rlp.EncodeToBytes(&block) if err != nil { @@ -85,7 +85,7 @@ func TestEIP2718BlockEncoding(t *testing.T) { 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))) + check("Size", block.Size(), uint64(len(blockEnc))) // Create legacy tx. to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") diff --git a/core/types/transaction.go b/core/types/transaction.go index acea3a989e1d..27b568836700 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -146,7 +146,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { var inner LegacyTx err := s.Decode(&inner) if err == nil { - tx.setDecoded(&inner, int(rlp.ListSize(size))) + tx.setDecoded(&inner, rlp.ListSize(size)) } return err default: @@ -157,7 +157,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { } inner, err := tx.decodeTyped(b) if err == nil { - tx.setDecoded(inner, len(b)) + tx.setDecoded(inner, uint64(len(b))) } return err } @@ -173,7 +173,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error { if err != nil { return err } - tx.setDecoded(&data, len(b)) + tx.setDecoded(&data, uint64(len(b))) return nil } // It's an EIP2718 typed transaction envelope. @@ -181,7 +181,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error { if err != nil { return err } - tx.setDecoded(inner, len(b)) + tx.setDecoded(inner, uint64(len(b))) return nil } @@ -205,11 +205,11 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { } // setDecoded sets the inner transaction and size after decoding. -func (tx *Transaction) setDecoded(inner TxData, size int) { +func (tx *Transaction) setDecoded(inner TxData, size uint64) { tx.inner = inner tx.time = time.Now() if size > 0 { - tx.size.Store(common.StorageSize(size)) + tx.size.Store(size) } } @@ -407,16 +407,26 @@ func (tx *Transaction) Hash() common.Hash { return h } -// Size returns the true RLP encoded storage size of the transaction, either by -// encoding and returning it, or returning a previously cached value. -func (tx *Transaction) Size() common.StorageSize { +// Size returns the true encoded storage size of the transaction, either by encoding +// and returning it, or returning a previously cached value. +func (tx *Transaction) Size() uint64 { if size := tx.size.Load(); size != nil { - return size.(common.StorageSize) + return size.(uint64) } + + // Cache miss, encode and cache. + // Note we rely on the assumption that all tx.inner values are RLP-encoded c := writeCounter(0) rlp.Encode(&c, &tx.inner) - tx.size.Store(common.StorageSize(c)) - return common.StorageSize(c) + size := uint64(c) + + // For typed transactions, the encoding also includes the leading type byte. + if tx.Type() != LegacyTxType { + size++ + } + + tx.size.Store(size) + return size } func (tx *Transaction) EffectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 569350784d05..6d870657adac 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -51,55 +51,58 @@ type txJSON struct { } // MarshalJSON marshals as JSON with a hash. -func (t *Transaction) MarshalJSON() ([]byte, error) { +func (tx *Transaction) MarshalJSON() ([]byte, error) { var enc txJSON // These are set for all tx types. - enc.Hash = t.Hash() - enc.Type = hexutil.Uint64(t.Type()) + enc.Hash = tx.Hash() + enc.Type = hexutil.Uint64(tx.Type()) - // Other fields are set conditionally depending on tx type. - switch tx := t.inner.(type) { + // Other fields are set conditionally depending on itx type. + switch itx := tx.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) + enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) + enc.To = tx.To() + enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.GasPrice = (*hexutil.Big)(itx.GasPrice) + enc.Value = (*hexutil.Big)(itx.Value) + enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.V = (*hexutil.Big)(itx.V) + enc.R = (*hexutil.Big)(itx.R) + enc.S = (*hexutil.Big)(itx.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) + enc.ChainID = (*hexutil.Big)(itx.ChainID) + enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) + enc.To = tx.To() + enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.GasPrice = (*hexutil.Big)(itx.GasPrice) + enc.Value = (*hexutil.Big)(itx.Value) + enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.AccessList = &itx.AccessList + enc.V = (*hexutil.Big)(itx.V) + enc.R = (*hexutil.Big)(itx.R) + enc.S = (*hexutil.Big)(itx.S) + case *DynamicFeeTx: - enc.ChainID = (*hexutil.Big)(tx.ChainID) - enc.AccessList = &tx.AccessList - enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) - enc.Gas = (*hexutil.Uint64)(&tx.Gas) - enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap) - enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap) - 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) + enc.ChainID = (*hexutil.Big)(itx.ChainID) + enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) + enc.To = tx.To() + enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap) + enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap) + enc.Value = (*hexutil.Big)(itx.Value) + enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.AccessList = &itx.AccessList + enc.V = (*hexutil.Big)(itx.V) + enc.R = (*hexutil.Big)(itx.R) + enc.S = (*hexutil.Big)(itx.S) } + return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (t *Transaction) UnmarshalJSON(input []byte) error { +func (tx *Transaction) UnmarshalJSON(input []byte) error { var dec txJSON if err := json.Unmarshal(input, &dec); err != nil { return err @@ -268,8 +271,7 @@ func (t *Transaction) UnmarshalJSON(input []byte) error { } // Now set the inner transaction. - t.setDecoded(inner, 0) + tx.setDecoded(inner, 0) - // TODO: check hash here? return nil } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 99d90fc03d29..3ff09a63f156 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -541,3 +541,71 @@ func assertEqual(orig *Transaction, cpy *Transaction) error { } return nil } + +func TestTransactionSizes(t *testing.T) { + signer := NewLondonSigner(big.NewInt(123)) + key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + to := common.HexToAddress("0x01") + for i, txdata := range []TxData{ + &AccessListTx{ + ChainID: big.NewInt(123), + Nonce: 0, + To: nil, + Value: big.NewInt(1000), + Gas: 21000, + GasPrice: big.NewInt(100000), + }, + &LegacyTx{ + Nonce: 1, + GasPrice: big.NewInt(500), + Gas: 1000000, + To: &to, + Value: big.NewInt(1), + }, + &AccessListTx{ + ChainID: big.NewInt(123), + Nonce: 1, + GasPrice: big.NewInt(500), + Gas: 1000000, + To: &to, + Value: big.NewInt(1), + AccessList: AccessList{ + AccessTuple{ + Address: common.HexToAddress("0x01"), + StorageKeys: []common.Hash{common.HexToHash("0x01")}, + }}, + }, + &DynamicFeeTx{ + ChainID: big.NewInt(123), + Nonce: 1, + Gas: 1000000, + To: &to, + Value: big.NewInt(1), + GasTipCap: big.NewInt(500), + GasFeeCap: big.NewInt(500), + }, + } { + tx, err := SignNewTx(key, signer, txdata) + if err != nil { + t.Fatalf("test %d: %v", i, err) + } + bin, _ := tx.MarshalBinary() + + // Check initial calc + if have, want := int(tx.Size()), len(bin); have != want { + t.Errorf("test %d: size wrong, have %d want %d", i, have, want) + } + // Check cached version too + if have, want := int(tx.Size()), len(bin); have != want { + t.Errorf("test %d: (cached) size wrong, have %d want %d", i, have, want) + } + // Check unmarshalled version too + utx := new(Transaction) + if err := utx.UnmarshalBinary(bin); err != nil { + t.Fatalf("test %d: failed to unmarshal tx: %v", i, err) + } + if have, want := int(utx.Size()), len(bin); have != want { + t.Errorf("test %d: (unmarshalled) size wrong, have %d want %d", i, have, want) + } + } +} diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index ab6dfc05bfb4..f4d87ccf7383 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -381,7 +381,7 @@ func (q *queue) Results(block bool) []*fetchResult { size += receipt.Size() } for _, tx := range result.Transactions { - size += tx.Size() + size += common.StorageSize(tx.Size()) } q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize diff --git a/eth/sync.go b/eth/sync.go index 5a0ea512f55f..85ff3a081548 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -78,7 +78,7 @@ func (pm *ProtocolManager) txsyncLoop() { pack.txs = pack.txs[:0] for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { pack.txs = append(pack.txs, s.txs[i]) - size += s.txs[i].Size() + size += common.StorageSize(s.txs[i].Size()) } // Remove the transactions that will be sent. s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] From a84169b7d55cf4c821263836ec77a0496d8131b6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 26 Nov 2025 12:36:09 +0800 Subject: [PATCH 02/10] core/types: add json marshalling and tweaks #27256 --- core/types/gen_access_tuple.go | 8 +-- core/types/transaction_marshalling.go | 86 +++++++++++++-------------- core/types/tx_access_list.go | 4 +- 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/core/types/gen_access_tuple.go b/core/types/gen_access_tuple.go index d23b32c00925..2a129530e194 100644 --- a/core/types/gen_access_tuple.go +++ b/core/types/gen_access_tuple.go @@ -12,8 +12,8 @@ import ( // 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"` + Address common.Address `json:"address" gencodec:"required"` + StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` } var enc AccessTuple enc.Address = a.Address @@ -24,8 +24,8 @@ func (a AccessTuple) MarshalJSON() ([]byte, error) { // 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"` + 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 { diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 6d870657adac..f52e8df65798 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -29,22 +29,19 @@ import ( type txJSON struct { Type hexutil.Uint64 `json:"type"` - // Common transaction fields: + ChainID *hexutil.Big `json:"chainId,omitempty"` Nonce *hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` - Gas *hexutil.Uint64 `json:"gas"` Value *hexutil.Big `json:"value"` - Data *hexutil.Bytes `json:"input"` + Input *hexutil.Bytes `json:"input"` + AccessList *AccessList `json:"accessList,omitempty"` 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"` @@ -65,7 +62,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.Gas = (*hexutil.Uint64)(&itx.Gas) enc.GasPrice = (*hexutil.Big)(itx.GasPrice) enc.Value = (*hexutil.Big)(itx.Value) - enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.Input = (*hexutil.Bytes)(&itx.Data) enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) enc.S = (*hexutil.Big)(itx.S) @@ -77,7 +74,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.Gas = (*hexutil.Uint64)(&itx.Gas) enc.GasPrice = (*hexutil.Big)(itx.GasPrice) enc.Value = (*hexutil.Big)(itx.Value) - enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.Input = (*hexutil.Bytes)(&itx.Data) enc.AccessList = &itx.AccessList enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) @@ -91,7 +88,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap) enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap) enc.Value = (*hexutil.Big)(itx.Value) - enc.Data = (*hexutil.Bytes)(&itx.Data) + enc.Input = (*hexutil.Bytes)(&itx.Data) enc.AccessList = &itx.AccessList enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) @@ -114,29 +111,30 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { 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") + if dec.To != nil { + itx.To = dec.To } - 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.GasPrice == nil { + return errors.New("missing required field 'gasPrice' in transaction") + } + itx.GasPrice = (*big.Int)(dec.GasPrice) if dec.Value == nil { return errors.New("missing required field 'value' in transaction") } itx.Value = (*big.Int)(dec.Value) - if dec.Data == nil { + if dec.Input == nil { return errors.New("missing required field 'input' in transaction") } - itx.Data = *dec.Data + itx.Data = *dec.Input + if dec.V == nil { return errors.New("missing required field 'v' in transaction") } @@ -159,37 +157,37 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { 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") + if dec.To != nil { + itx.To = dec.To } - 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.GasPrice == nil { + return errors.New("missing required field 'gasPrice' in transaction") + } + itx.GasPrice = (*big.Int)(dec.GasPrice) if dec.Value == nil { return errors.New("missing required field 'value' in transaction") } itx.Value = (*big.Int)(dec.Value) - if dec.Data == nil { + if dec.Input == nil { return errors.New("missing required field 'input' in transaction") } - itx.Data = *dec.Data + itx.Data = *dec.Input + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.V == nil { return errors.New("missing required field 'v' in transaction") } @@ -212,21 +210,21 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { case DynamicFeeTxType: var itx DynamicFeeTx 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.To != nil { + itx.To = dec.To + } + if dec.Gas == nil { + return errors.New("missing required field 'gas' for txdata") + } + itx.Gas = uint64(*dec.Gas) if dec.MaxPriorityFeePerGas == nil { return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") } @@ -235,18 +233,18 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'maxFeePerGas' for txdata") } itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas) - if dec.Gas == nil { - return errors.New("missing required field 'gas' for txdata") - } - 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 { + if dec.Input == nil { return errors.New("missing required field 'input' in transaction") } - itx.Data = *dec.Data + itx.Data = *dec.Input + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.V == nil { return errors.New("missing required field 'v' in transaction") } diff --git a/core/types/tx_access_list.go b/core/types/tx_access_list.go index 8c9577ef1535..8486510ba595 100644 --- a/core/types/tx_access_list.go +++ b/core/types/tx_access_list.go @@ -29,8 +29,8 @@ 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"` + 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. From ef052219f46a8d5eaed97f252c2e591eb733eee4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 26 Nov 2025 11:03:20 +0800 Subject: [PATCH 03/10] core/types: support yParity field in JSON transactions #27744 --- core/types/transaction.go | 3 + core/types/transaction_marshalling.go | 70 +++++++++--- internal/ethapi/api.go | 7 ++ internal/ethapi/api_test.go | 159 +++++++++++++++++++++++++- 4 files changed, 214 insertions(+), 25 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 27b568836700..72d37bf54d2d 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -38,6 +38,9 @@ var ( ErrTxTypeNotSupported = errors.New("transaction type not supported") ErrGasFeeCapTooLow = errors.New("fee cap less than base fee") errShortTypedTx = errors.New("typed transaction too short") + errInvalidYParity = errors.New("'yParity' field must be 0 or 1") + errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") + errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") skipNonceDestinationAddress = map[common.Address]bool{ common.XDCXAddrBinary: true, diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index f52e8df65798..572af2c47980 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -42,11 +42,32 @@ type txJSON struct { V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` // Only used for encoding: Hash common.Hash `json:"hash"` } +// yParityValue returns the YParity value from JSON. For backwards-compatibility reasons, +// this can be given in the 'v' field or the 'yParity' field. If both exist, they must match. +func (tx *txJSON) yParityValue() (*big.Int, error) { + if tx.YParity != nil { + val := uint64(*tx.YParity) + if val != 0 && val != 1 { + return nil, errInvalidYParity + } + bigval := new(big.Int).SetUint64(val) + if tx.V != nil && tx.V.ToInt().Cmp(bigval) != 0 { + return nil, errVYParityMismatch + } + return bigval, nil + } + if tx.V != nil { + return tx.V.ToInt(), nil + } + return nil, errVYParityMissing +} + // MarshalJSON marshals as JSON with a hash. func (tx *Transaction) MarshalJSON() ([]byte, error) { var enc txJSON @@ -79,6 +100,8 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) enc.S = (*hexutil.Big)(itx.S) + yparity := itx.V.Uint64() + enc.YParity = (*hexutil.Uint64)(&yparity) case *DynamicFeeTx: enc.ChainID = (*hexutil.Big)(itx.ChainID) @@ -93,6 +116,8 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) enc.S = (*hexutil.Big)(itx.S) + yparity := itx.V.Uint64() + enc.YParity = (*hexutil.Uint64)(&yparity) } return json.Marshal(&enc) @@ -101,7 +126,8 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (tx *Transaction) UnmarshalJSON(input []byte) error { var dec txJSON - if err := json.Unmarshal(input, &dec); err != nil { + err := json.Unmarshal(input, &dec) + if err != nil { return err } @@ -135,20 +161,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { } itx.Data = *dec.Input - if dec.V == nil { - return errors.New("missing required field 'v' in transaction") - } - itx.V = (*big.Int)(dec.V) + // signature R if dec.R == nil { return errors.New("missing required field 'r' in transaction") } itx.R = (*big.Int)(dec.R) + // signature S 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 { + // signature V + if dec.V == nil { + return errors.New("missing required field 'v' in transaction") + } + itx.V = (*big.Int)(dec.V) + if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil { return err } @@ -188,20 +216,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { itx.AccessList = *dec.AccessList } - if dec.V == nil { - return errors.New("missing required field 'v' in transaction") - } - itx.V = (*big.Int)(dec.V) + // signature R if dec.R == nil { return errors.New("missing required field 'r' in transaction") } itx.R = (*big.Int)(dec.R) + // signature S 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 { + // signature V + itx.V, err = dec.yParityValue() + if err != nil { + return err + } + if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil { return err } @@ -245,20 +275,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { itx.AccessList = *dec.AccessList } - if dec.V == nil { - return errors.New("missing required field 'v' in transaction") - } - itx.V = (*big.Int)(dec.V) + // signature R if dec.R == nil { return errors.New("missing required field 'r' in transaction") } itx.R = (*big.Int)(dec.R) + // signature S 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 { + // signature V + itx.V, err = dec.yParityValue() + if err != nil { + return err + } + if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil { return err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 38829caf9098..fc4d0243210b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1670,6 +1670,7 @@ type RPCTransaction struct { V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` } // newRPCTransaction returns a transaction that will serialize to the RPC @@ -1697,15 +1698,21 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } + switch tx.Type() { case types.AccessListTxType: al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity + case types.DynamicFeeTxType: al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 8f5eb95b4e35..a995985078c5 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -25,6 +25,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" ) @@ -92,20 +93,168 @@ func TestRPCMarshalBlock(t *testing.T) { { inclTx: false, fullTx: false, - want: `{"difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x64","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","penalties":"0x","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x299","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e","uncles":[],"validator":"0x","validators":"0x"}`, + want: `{ + "difficulty":"0x0", + "extraData":"0x", + "gasLimit":"0x0", + "gasUsed":"0x0", + "hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner":"0x0000000000000000000000000000000000000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce":"0x0000000000000000", + "number":"0x64", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "penalties":"0x", + "receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size":"0x299", + "stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp":"0x0", + "transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e", + "uncles":[], + "validator":"0x", + "validators":"0x" + }`, }, // only tx hashes { inclTx: true, fullTx: false, - want: `{"difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x64","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","penalties":"0x","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x299","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactions":["0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605","0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4","0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5","0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1"],"transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e","uncles":[],"validator":"0x","validators":"0x"}`, + want: `{ + "difficulty":"0x0", + "extraData":"0x", + "gasLimit":"0x0", + "gasUsed":"0x0", + "hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner":"0x0000000000000000000000000000000000000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce":"0x0000000000000000", + "number":"0x64", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "penalties":"0x", + "receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size":"0x299", + "stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp":"0x0", + "transactions": [ + "0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605", + "0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4", + "0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5", + "0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1" + ], + "transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e", + "uncles":[], + "validator":"0x", + "validators":"0x" + }`, }, // full tx details { inclTx: true, fullTx: true, - want: `{"difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x64","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","penalties":"0x","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x299","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactions":[{"blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","blockNumber":"0x64","from":"0x0000000000000000000000000000000000000000","gas":"0x457","gasPrice":"0x2b67","hash":"0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605","input":"0x111111","nonce":"0x1","to":"0x0000000000000000000000000000000000000011","transactionIndex":"0x0","value":"0x6f","type":"0x1","accessList":[],"chainId":"0x539","v":"0x0","r":"0x0","s":"0x0"},{"blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","blockNumber":"0x64","from":"0x0000000000000000000000000000000000000000","gas":"0x457","gasPrice":"0x2b67","hash":"0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4","input":"0x111111","nonce":"0x2","to":"0x0000000000000000000000000000000000000011","transactionIndex":"0x1","value":"0x6f","type":"0x0","v":"0x0","r":"0x0","s":"0x0"},{"blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","blockNumber":"0x64","from":"0x0000000000000000000000000000000000000000","gas":"0x457","gasPrice":"0x2b67","hash":"0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5","input":"0x111111","nonce":"0x3","to":"0x0000000000000000000000000000000000000011","transactionIndex":"0x2","value":"0x6f","type":"0x1","accessList":[],"chainId":"0x539","v":"0x0","r":"0x0","s":"0x0"},{"blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50","blockNumber":"0x64","from":"0x0000000000000000000000000000000000000000","gas":"0x457","gasPrice":"0x2b67","hash":"0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1","input":"0x111111","nonce":"0x4","to":"0x0000000000000000000000000000000000000011","transactionIndex":"0x3","value":"0x6f","type":"0x0","v":"0x0","r":"0x0","s":"0x0"}],"transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e","uncles":[],"validator":"0x","validators":"0x"}`, + want: `{ + "difficulty":"0x0", + "extraData":"0x", + "gasLimit":"0x0", + "gasUsed":"0x0", + "hash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner":"0x0000000000000000000000000000000000000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce":"0x0000000000000000", + "number":"0x64", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "penalties":"0x", + "receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size":"0x299", + "stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp":"0x0", + "transactions": [ + { + "blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "blockNumber":"0x64", + "from":"0x0000000000000000000000000000000000000000", + "gas":"0x457", + "gasPrice":"0x2b67", + "hash":"0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605", + "input":"0x111111", + "nonce":"0x1", + "to":"0x0000000000000000000000000000000000000011", + "transactionIndex":"0x0", + "value":"0x6f", + "type":"0x1", + "accessList":[], + "chainId":"0x539", + "v":"0x0", + "r":"0x0", + "s":"0x0", + "yParity": "0x0" + }, + { + "blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "blockNumber":"0x64", + "from":"0x0000000000000000000000000000000000000000", + "gas":"0x457", + "gasPrice":"0x2b67", + "hash":"0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4", + "input":"0x111111", + "nonce":"0x2", + "to":"0x0000000000000000000000000000000000000011", + "transactionIndex":"0x1", + "value":"0x6f", + "type":"0x0", + "v":"0x0", + "r":"0x0", + "s":"0x0" + }, + { + "blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "blockNumber":"0x64", + "from":"0x0000000000000000000000000000000000000000", + "gas":"0x457", + "gasPrice":"0x2b67", + "hash":"0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5", + "input":"0x111111", + "nonce":"0x3", + "to":"0x0000000000000000000000000000000000000011", + "transactionIndex":"0x2", + "value":"0x6f", + "type":"0x1", + "accessList":[], + "chainId":"0x539", + "v":"0x0", + "r":"0x0", + "s":"0x0", + "yParity": "0x0" + }, + { + "blockHash":"0x2cb4e4b5b5be5a2520377e87e8d7d2cf83fc0783fa6518d67b9606d3c5317b50", + "blockNumber":"0x64", + "from":"0x0000000000000000000000000000000000000000", + "gas":"0x457", + "gasPrice":"0x2b67", + "hash":"0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1", + "input":"0x111111", + "nonce":"0x4", + "to":"0x0000000000000000000000000000000000000011", + "transactionIndex":"0x3", + "value":"0x6f", + "type":"0x0", + "v":"0x0", + "r":"0x0", + "s":"0x0" + } + ], + "transactionsRoot":"0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e", + "uncles":[], + "validator":"0x", + "validators":"0x" + }`, }, } @@ -116,8 +265,6 @@ func TestRPCMarshalBlock(t *testing.T) { t.Errorf("test %d: json marshal error: %v", i, err) continue } - if have := string(out); have != tc.want { - t.Errorf("test %d:\nwant: %s\nhave: %s", i, tc.want, have) - } + require.JSONEqf(t, tc.want, string(out), "test %d", i) } } From 0f3508de2efdbbd5d9e3708824ae29ea4d9022ab Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 19 Nov 2025 12:52:20 +0800 Subject: [PATCH 04/10] core/types: add derived chain ID to LegacyTx JSON encoding #27452 --- core/types/transaction_marshalling.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 572af2c47980..b965ce36e12c 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -87,6 +87,9 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.V = (*hexutil.Big)(itx.V) enc.R = (*hexutil.Big)(itx.R) enc.S = (*hexutil.Big)(itx.S) + if tx.Protected() { + enc.ChainID = (*hexutil.Big)(tx.ChainId()) + } case *AccessListTx: enc.ChainID = (*hexutil.Big)(itx.ChainID) From 46f76efc9efd887161da7a96a4bda76eb5a84399 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 19 Nov 2025 14:16:20 +0800 Subject: [PATCH 05/10] core/types: implement encode/decode for TxData interface #27841 --- core/blockchain.go | 17 +++++++++++------ core/types/transaction.go | 16 +++++++++------- core/types/tx_access_list.go | 10 ++++++++++ core/types/tx_dynamic_fee.go | 10 ++++++++++ core/types/tx_legacy.go | 9 +++++++++ 5 files changed, 49 insertions(+), 13 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 5aa6645de60c..86525174dad7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1293,12 +1293,17 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ defer bc.wg.Done() // Do a sanity check that the provided chain is actually ordered and linked - for i := 1; i < len(blockChain); i++ { - if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() { - log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(), - "prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash()) - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(), - blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) + for i, block := range blockChain { + if i != 0 { + prev := blockChain[i-1] + if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { + log.Error("Non contiguous receipt insert", + "number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(), + "prevnumber", prev.Number(), "prevhash", prev.Hash()) + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", + i-1, prev.NumberU64(), prev.Hash().Bytes()[:4], + i, block.NumberU64(), block.Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) + } } } diff --git a/core/types/transaction.go b/core/types/transaction.go index 72d37bf54d2d..ee72b00074c5 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -103,6 +103,9 @@ type TxData interface { // copy of the computed value, i.e. callers are allowed to mutate the result. // Method implementations can use 'dst' to store the result. effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int + + encode(*bytes.Buffer) error + decode([]byte) error } // EncodeRLP implements rlp.Encoder @@ -123,7 +126,7 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error { // 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) + return tx.inner.encode(w) } // MarshalBinary returns the canonical encoding of the transaction. @@ -193,18 +196,17 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { if len(b) <= 1 { return nil, errShortTypedTx } + var inner TxData switch b[0] { case AccessListTxType: - var inner AccessListTx - err := rlp.DecodeBytes(b[1:], &inner) - return &inner, err + inner = new(AccessListTx) case DynamicFeeTxType: - var inner DynamicFeeTx - err := rlp.DecodeBytes(b[1:], &inner) - return &inner, err + inner = new(DynamicFeeTx) default: return nil, ErrTxTypeNotSupported } + err := inner.decode(b[1:]) + return inner, err } // setDecoded sets the inner transaction and size after decoding. diff --git a/core/types/tx_access_list.go b/core/types/tx_access_list.go index 8486510ba595..975dd70bd1bb 100644 --- a/core/types/tx_access_list.go +++ b/core/types/tx_access_list.go @@ -17,9 +17,11 @@ package types import ( + "bytes" "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/rlp" ) //go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go @@ -117,3 +119,11 @@ func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) { tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s } + +func (tx *AccessListTx) encode(b *bytes.Buffer) error { + return rlp.Encode(b, tx) +} + +func (tx *AccessListTx) decode(input []byte) error { + return rlp.DecodeBytes(input, tx) +} diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go index 0961ac1e7884..a9b9f37d894a 100644 --- a/core/types/tx_dynamic_fee.go +++ b/core/types/tx_dynamic_fee.go @@ -17,9 +17,11 @@ package types import ( + "bytes" "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/rlp" ) type DynamicFeeTx struct { @@ -112,3 +114,11 @@ func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) { tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s } + +func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error { + return rlp.Encode(b, tx) +} + +func (tx *DynamicFeeTx) decode(input []byte) error { + return rlp.DecodeBytes(input, tx) +} diff --git a/core/types/tx_legacy.go b/core/types/tx_legacy.go index 752aff10dd5e..8583668f7467 100644 --- a/core/types/tx_legacy.go +++ b/core/types/tx_legacy.go @@ -17,6 +17,7 @@ package types import ( + "bytes" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -115,3 +116,11 @@ func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) { tx.V, tx.R, tx.S = v, r, s } + +func (tx *LegacyTx) encode(*bytes.Buffer) error { + panic("encode called on LegacyTx") +} + +func (tx *LegacyTx) decode([]byte) error { + panic("decode called on LegacyTx)") +} From b4ee7da8e48206c50cbd283183eb1b70920a51d4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 26 Nov 2025 15:42:41 +0800 Subject: [PATCH 06/10] internal/ethapi: eth api changes needed #27928 --- internal/ethapi/api.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fc4d0243210b..c9e0c92f12ae 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1718,12 +1718,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { // price = min(tip, gasFeeCap - baseFee) + baseFee - price := new(big.Int).Add(tx.GasTipCap(), baseFee) - txGasFeeCap := tx.GasFeeCap() - if price.Cmp(txGasFeeCap) > 0 { - price = txGasFeeCap - } - result.GasPrice = (*hexutil.Big)(price) + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } @@ -1731,6 +1726,18 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber return result } +// effectiveGasPrice computes the transaction gas fee, based on the given basefee value. +// +// price = min(gasTipCap + baseFee, gasFeeCap) +func effectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int { + fee := tx.GasTipCap() + fee = fee.Add(fee, baseFee) + if tx.GasFeeCapIntCmp(fee) < 0 { + return tx.GasFeeCap() + } + return fee +} + // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction { var baseFee *big.Int From 514fe8424de28d85fa4f8a19e8cb7a447aed1f91 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Nov 2025 07:30:11 +0800 Subject: [PATCH 07/10] core/state: move state log mechanism to a separate layer 30569 --- accounts/abi/bind/backends/simulated.go | 3 +- consensus/XDPoS/XDPoS.go | 3 +- consensus/XDPoS/engines/engine_v1/engine.go | 5 +- consensus/XDPoS/engines/engine_v2/engine.go | 5 +- consensus/clique/clique.go | 3 +- consensus/consensus.go | 3 +- consensus/ethash/consensus.go | 5 +- consensus/misc/dao.go | 9 +- core/blockchain.go | 1 - core/state/database.go | 6 + core/state/journal.go | 18 ++ core/state/state_object.go | 42 +-- core/state/state_test.go | 15 +- core/state/statedb.go | 111 ++++---- core/state/statedb_hooked.go | 259 ++++++++++++++++++ core/state/statedb_hooked_test.go | 129 +++++++++ core/state/statedb_test.go | 29 +- core/state/sync_test.go | 3 +- core/state_processor.go | 101 +++++-- core/vm/instructions.go | 2 +- core/vm/interface.go | 28 +- eth/hooks/engine_v1_hooks.go | 3 +- eth/hooks/engine_v2_hooks.go | 3 +- eth/tracers/api.go | 1 - .../internal/tracetest/calltrace_test.go | 22 +- .../internal/tracetest/flat_calltrace_test.go | 1 - .../internal/tracetest/prestate_test.go | 11 - eth/tracers/logger/logger_test.go | 8 +- internal/ethapi/api.go | 13 +- internal/ethapi/simulate.go | 11 +- 30 files changed, 676 insertions(+), 177 deletions(-) create mode 100644 core/state/statedb_hooked.go create mode 100644 core/state/statedb_hooked_test.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 13eec1a070d9..f39c770c1109 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -43,7 +43,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/bloombits" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" - "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -738,7 +737,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Set infinite balance to the fake caller account. from := stateDB.GetOrNewStateObject(call.From) - from.SetBalance(math.MaxBig256, tracing.BalanceChangeUnspecified) + from.SetBalance(math.MaxBig256) // Execute the call. msg := &core.Message{ diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index a9e20b4c4216..09dfec9f5b7e 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/log" @@ -276,7 +277,7 @@ func (x *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. -func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { switch x.config.BlockConsensusVersion(header.Number) { case params.ConsensusEngineVersion2: return x.EngineV2.Finalize(chain, header, state, parentState, txs, uncles, receipts) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 2b0f77cef5da..b1b56da27958 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -21,6 +21,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" @@ -53,7 +54,7 @@ type XDPoS_v1 struct { signFn clique.SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields - HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) + HookReward func(chain consensus.ChainReader, state vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error) HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) @@ -823,7 +824,7 @@ func (x *XDPoS_v1) UpdateMasternodes(chain consensus.ChainReader, header *types. // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. -func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { // set block reward number := header.Number.Uint64() rCheckpoint := chain.Config().XDPoS.RewardCheckpoint diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 633467a4862b..8d1c1a6dc2b2 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -23,6 +23,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" @@ -74,7 +75,7 @@ type XDPoS_v2 struct { latestReward map[string]interface{} latestRewardBlocknum uint64 - HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) + HookReward func(chain consensus.ChainReader, state vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) ForensicsProcessor *Forensics @@ -406,7 +407,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. -func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { // set block reward isEpochSwitch, _, err := x.IsEpochSwitch(header) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 97454b601294..37a651f9dd70 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -34,6 +34,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" @@ -585,7 +586,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. -func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { // No block rewards in PoA, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) diff --git a/consensus/consensus.go b/consensus/consensus.go index 080b22406f54..d50479b35976 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -23,6 +23,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" ) @@ -83,7 +84,7 @@ type Engine interface { // and assembles the final block. // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - Finalize(chain ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, + Finalize(chain ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) // Seal generates a new block for the given input block with the local miner's diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c70f52b88afb..323f92a64691 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/trie" mapset "github.com/deckarep/golang-set/v2" @@ -435,7 +436,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) // Finalize implements consensus.Engine, accumulating the block and uncle rewards, // setting the final state and assembling the block. -func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { +func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) @@ -453,7 +454,7 @@ var ( // AccumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) { +func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index da873dd0b2a1..f25e75184998 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -21,9 +21,9 @@ import ( "errors" "math/big" - "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" ) @@ -73,7 +73,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) // ApplyDAOHardFork modifies the state database according to the DAO hard-fork // rules, transferring all balances of a set of DAO accounts to a single refund // contract. -func ApplyDAOHardFork(statedb *state.StateDB) { +func ApplyDAOHardFork(statedb vm.StateDB) { // Retrieve the contract to refund balances into if !statedb.Exist(params.DAORefundContract) { statedb.CreateAccount(params.DAORefundContract) @@ -81,7 +81,8 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract) - statedb.SetBalance(addr, new(big.Int), tracing.BalanceDecreaseDaoAccount) + balance := statedb.GetBalance(addr) + statedb.AddBalance(params.DAORefundContract, balance, tracing.BalanceIncreaseDaoContract) + statedb.SubBalance(addr, balance, tracing.BalanceDecreaseDaoAccount) } } diff --git a/core/blockchain.go b/core/blockchain.go index 86525174dad7..64c2bcd1fb9e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1789,7 +1789,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] if err != nil { return it.index, events, coalescedLogs, err } - statedb.SetLogger(bc.logger) // If we have a followup block, run that against the current state to pre-cache // transactions and probabilistically some of the account/storage trie nodes. diff --git a/core/state/database.go b/core/state/database.go index efb2522196af..e3b63d48fa00 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -132,6 +132,12 @@ func NewDatabase(db ethdb.Database) Database { return NewDatabaseWithConfig(db, nil) } +// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching +// db by using an ephemeral memory db with default config for testing. +func NewDatabaseForTesting() Database { + return NewDatabase(rawdb.NewMemoryDatabase()) +} + // NewDatabaseWithConfig creates a backing store for state. The returned database // is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a // large memory cache. diff --git a/core/state/journal.go b/core/state/journal.go index 6d266a0542c8..1ffeef1ad0cd 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -85,11 +85,21 @@ func (j *journal) length() int { return len(j.entries) } +func (j *journal) createContract(addr common.Address) { + j.append(createContractChange{account: addr}) +} + type ( // Changes to the account trie. createObjectChange struct { account common.Address } + // createContractChange represents an account becoming a contract-account. + // This event happens prior to executing initcode. The journal-event simply + // manages the created-flag, in order to allow same-tx destruction. + createContractChange struct { + account common.Address + } resetObjectChange struct { account common.Address prev *stateObject @@ -156,6 +166,14 @@ func (ch createObjectChange) dirtied() *common.Address { return &ch.account } +func (ch createContractChange) revert(s *StateDB) { + s.getStateObject(ch.account).created = false +} + +func (ch createContractChange) dirtied() *common.Address { + return nil +} + func (ch resetObjectChange) revert(s *StateDB) { s.setStateObject(ch.prev) if !ch.prevdestruct { diff --git a/core/state/state_object.go b/core/state/state_object.go index 0c726bc042d7..bc0ab83fb389 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -25,7 +25,6 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/rlp" @@ -218,11 +217,12 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has } // SetState updates a value in account storage. -func (s *stateObject) SetState(db Database, key, value common.Hash) { - // If the new value is the same as old, don't set +func (s *stateObject) SetState(db Database, key, value common.Hash) common.Hash { + // If the new value is the same as old, don't set. Otherwise, track only the + // dirty changes, supporting reverting all of it back to no change. prev := s.GetState(db, key) if prev == value { - return + return prev } // New value is different, update and journal the change s.db.journal.append(storageChange{ @@ -230,10 +230,8 @@ func (s *stateObject) SetState(db Database, key, value common.Hash) { key: key, prevalue: prev, }) - if s.db.logger != nil && s.db.logger.OnStorageChange != nil { - s.db.logger.OnStorageChange(s.address, key, prev, value) - } s.setState(key, value) + return prev } func (s *stateObject) setState(key, value common.Hash) { @@ -340,36 +338,28 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *big.Int, reason tracing.BalanceChangeReason) { +// returns the previous balance +func (s *stateObject) AddBalance(amount *big.Int) *big.Int { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.Sign() == 0 { if s.empty() { s.touch() } - return + return new(big.Int).Set(s.Balance()) } - s.SetBalance(new(big.Int).Add(s.Balance(), amount), reason) + return s.SetBalance(new(big.Int).Add(s.Balance(), amount)) } -// SubBalance removes amount from s's balance. -// It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) { - if amount.Sign() == 0 { - return - } - s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason) -} - -func (s *stateObject) SetBalance(amount *big.Int, reason tracing.BalanceChangeReason) { +// SetBalance sets the balance for the object, and returns the previous balance. +func (s *stateObject) SetBalance(amount *big.Int) *big.Int { + prev := new(big.Int).Set(s.data.Balance) s.db.journal.append(balanceChange{ account: s.address, prev: new(big.Int).Set(s.data.Balance), }) - if s.db.logger != nil && s.db.logger.OnBalanceChange != nil { - s.db.logger.OnBalanceChange(s.address, s.Balance(), amount, reason) - } s.setBalance(amount) + return prev } func (s *stateObject) setBalance(amount *big.Int) { @@ -439,9 +429,6 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { account: s.address, prevCode: prevCode, }) - if s.db.logger != nil && s.db.logger.OnCodeChange != nil { - s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevCode, codeHash, code) - } s.setCode(codeHash, code) } @@ -456,9 +443,6 @@ func (s *stateObject) SetNonce(nonce uint64) { account: s.address, prev: s.data.Nonce, }) - if s.db.logger != nil && s.db.logger.OnNonceChange != nil { - s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce) - } s.setNonce(nonce) } diff --git a/core/state/state_test.go b/core/state/state_test.go index ae4fa91d401e..7dcd1447a524 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -24,7 +24,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" @@ -49,11 +48,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22)) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -101,13 +100,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22)) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44)) obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(big.NewInt(1337), tracing.BalanceChangeUnspecified) + obj4.AddBalance(big.NewInt(1337)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -203,7 +202,7 @@ func TestSnapshot2(t *testing.T) { // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) - so0.SetBalance(big.NewInt(42), tracing.BalanceChangeUnspecified) + so0.SetBalance(big.NewInt(42)) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.selfDestructed = false @@ -215,7 +214,7 @@ func TestSnapshot2(t *testing.T) { // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) - so1.SetBalance(big.NewInt(52), tracing.BalanceChangeUnspecified) + so1.SetBalance(big.NewInt(52)) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.selfDestructed = true diff --git a/core/state/statedb.go b/core/state/statedb.go index 184950070930..f575a171443b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -47,9 +47,8 @@ type revision struct { // * Contracts // * Accounts type StateDB struct { - db Database - trie Trie - logger *tracing.Hooks + db Database + trie Trie // originalRoot is the pre-state root, before any changes were made. // It will be updated when the Commit is called. @@ -139,11 +138,6 @@ func New(root common.Hash, db Database) (*StateDB, error) { }, nil } -// SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l *tracing.Hooks) { - s.logger = l -} - // setError remembers the first non-nil error it is called with. func (s *StateDB) setError(err error) { if s.dbErr == nil { @@ -183,9 +177,6 @@ func (s *StateDB) AddLog(log *types.Log) { log.TxHash = s.thash log.TxIndex = uint(s.txIndex) log.Index = s.logSize - if s.logger != nil && s.logger.OnLog != nil { - s.logger.OnLog(log) - } s.logs[s.thash] = append(s.logs[s.thash], log) s.logSize++ } @@ -390,25 +381,31 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int { stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - stateObject.AddBalance(amount, reason) + if stateObject == nil { + return new(big.Int) } + return stateObject.AddBalance(amount) } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int { stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - stateObject.SubBalance(amount, reason) + if stateObject == nil { + return new(big.Int) } + prev := stateObject.Balance() + if amount.Sign() == 0 { + return new(big.Int).Set(prev) + } + return stateObject.SetBalance(new(big.Int).Sub(prev, amount)) } func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetBalance(amount, reason) + stateObject.SetBalance(amount) } } @@ -426,11 +423,11 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) { } } -func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - stateObject.SetState(s.db, key, value) +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash { + if stateObject := s.GetOrNewStateObject(addr); stateObject != nil { + return stateObject.SetState(s.db, key, value) } + return common.Hash{} } // SetStorage replaces the entire storage for the specified account with given @@ -458,7 +455,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common if obj != nil { newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code) newObj.SetNonce(obj.Nonce()) - newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified) + newObj.SetBalance(obj.Balance()) } } @@ -467,36 +464,40 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after SelfDestruct. -func (s *StateDB) SelfDestruct(addr common.Address) { +func (s *StateDB) SelfDestruct(addr common.Address) *big.Int { stateObject := s.getStateObject(addr) + prevBalance := new(big.Int) if stateObject == nil { - return - } - var ( - prev = new(big.Int).Set(stateObject.Balance()) - n = new(big.Int) - ) - s.journal.append(selfDestructChange{ - account: addr, - prev: stateObject.selfDestructed, - prevbalance: prev, - }) - if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 { - s.logger.OnBalanceChange(addr, prev, n, tracing.BalanceDecreaseSelfdestruct) + return prevBalance + } + prevBalance.Set(stateObject.Balance()) + // Regardless of whether it is already destructed or not, we do have to + // journal the balance-change, if we set it to zero here. + if prevBalance.Sign() != 0 { + stateObject.SetBalance(new(big.Int)) + } + // If it is already marked as self-destructed, we do not need to add it + // for journalling a second time. + if !stateObject.selfDestructed { + s.journal.append(selfDestructChange{ + account: addr, + prev: stateObject.selfDestructed, + prevbalance: prevBalance, + }) + stateObject.markSelfdestructed() } - stateObject.markSelfdestructed() - stateObject.data.Balance = n + return prevBalance } -func (s *StateDB) Selfdestruct6780(addr common.Address) { +func (s *StateDB) SelfDestruct6780(addr common.Address) (*big.Int, bool) { stateObject := s.getStateObject(addr) if stateObject == nil { - return + return new(big.Int), false } - if stateObject.created { - s.SelfDestruct(addr) + return s.SelfDestruct(addr), true } + return new(big.Int).Set(stateObject.Balance()), false } // SetTransientState sets transient storage for a given account. It @@ -653,6 +654,19 @@ func (s *StateDB) CreateAccount(addr common.Address) { } } +// CreateContract is used whenever a contract is created. This may be preceded +// by CreateAccount, but that is not required if it already existed in the +// state due to funds sent beforehand. +// This operation sets the 'newContract'-flag, which is required in order to +// correctly handle EIP-6780 'delete-in-same-transaction' logic. +func (s *StateDB) CreateContract(addr common.Address) { + obj := s.getStateObject(addr) + if !obj.created { + obj.created = true + s.journal.createContract(addr) + } +} + func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { so := s.getStateObject(addr) if so == nil { @@ -801,14 +815,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { obj.deleted = true - // We need to maintain account deletions explicitly (will remain - // set indefinitely). - s.stateObjectsDestruct[obj.address] = struct{}{} - - // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { - s.logger.OnBalanceChange(obj.address, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + // set indefinitely). Note only the first occurred self-destruct + // event is tracked. + if _, ok := s.stateObjectsDestruct[obj.address]; !ok { + s.stateObjectsDestruct[obj.address] = struct{}{} } } else { obj.finalise() diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go new file mode 100644 index 000000000000..9cd358bc93cc --- /dev/null +++ b/core/state/statedb_hooked.go @@ -0,0 +1,259 @@ +// Copyright 2024 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 . + +package state + +import ( + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/tracing" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" +) + +// hookedStateDB represents a statedb which emits calls to tracing-hooks +// on state operations. +type hookedStateDB struct { + inner *StateDB + hooks *tracing.Hooks +} + +// NewHookedState wraps the given stateDb with the given hooks +func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB { + s := &hookedStateDB{stateDb, hooks} + if s.hooks == nil { + s.hooks = new(tracing.Hooks) + } + return s +} + +func (s *hookedStateDB) CreateAccount(addr common.Address) { + s.inner.CreateAccount(addr) +} + +func (s *hookedStateDB) CreateContract(addr common.Address) { + s.inner.CreateContract(addr) +} + +func (s *hookedStateDB) GetBalance(addr common.Address) *big.Int { + return s.inner.GetBalance(addr) +} + +func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { + return s.inner.GetNonce(addr) +} + +func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { + return s.inner.GetCodeHash(addr) +} + +func (s *hookedStateDB) GetCode(addr common.Address) []byte { + return s.inner.GetCode(addr) +} + +func (s *hookedStateDB) GetCodeSize(addr common.Address) int { + return s.inner.GetCodeSize(addr) +} + +func (s *hookedStateDB) AddRefund(u uint64) { + s.inner.AddRefund(u) +} + +func (s *hookedStateDB) SubRefund(u uint64) { + s.inner.SubRefund(u) +} + +func (s *hookedStateDB) GetRefund() uint64 { + return s.inner.GetRefund() +} + +func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetCommittedState(addr, hash) +} + +func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetState(addr, hash) +} + +func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash { + return s.inner.GetStorageRoot(addr) +} + +func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { + return s.inner.GetTransientState(addr, key) +} + +func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) { + s.inner.SetTransientState(addr, key, value) +} + +func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool { + return s.inner.HasSelfDestructed(addr) +} + +func (s *hookedStateDB) Exist(addr common.Address) bool { + return s.inner.Exist(addr) +} + +func (s *hookedStateDB) Empty(addr common.Address) bool { + return s.inner.Empty(addr) +} + +func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool { + return s.inner.AddressInAccessList(addr) +} + +func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { + return s.inner.SlotInAccessList(addr, slot) +} + +func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) { + s.inner.AddAddressToAccessList(addr) +} + +func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + s.inner.AddSlotToAccessList(addr, slot) +} + +func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { + s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses) +} + +func (s *hookedStateDB) RevertToSnapshot(i int) { + s.inner.RevertToSnapshot(i) +} + +func (s *hookedStateDB) Snapshot() int { + return s.inner.Snapshot() +} + +func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) { + s.inner.Snapshot() +} + +func (s *hookedStateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int { + prev := s.inner.SubBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && amount.Sign() != 0 { + newBalance := new(big.Int).Sub(prev, amount) + s.hooks.OnBalanceChange(addr, prev, newBalance, reason) + } + return prev +} + +func (s *hookedStateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int { + prev := s.inner.AddBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && amount.Sign() != 0 { + newBalance := new(big.Int).Add(prev, amount) + s.hooks.OnBalanceChange(addr, prev, newBalance, reason) + } + return prev +} + +func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) { + s.inner.SetNonce(address, nonce) + if s.hooks.OnNonceChange != nil { + s.hooks.OnNonceChange(address, nonce-1, nonce) + } +} + +func (s *hookedStateDB) SetCode(address common.Address, code []byte) { + s.inner.SetCode(address, code) + if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code) + } +} + +func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { + prev := s.inner.SetState(address, key, value) + if s.hooks.OnStorageChange != nil && prev != value { + s.hooks.OnStorageChange(address, key, prev, value) + } + return prev +} + +func (s *hookedStateDB) SelfDestruct(address common.Address) *big.Int { + prev := s.inner.SelfDestruct(address) + if prev.Sign() != 0 { + if s.hooks.OnBalanceChange != nil { + s.hooks.OnBalanceChange(address, prev, new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + } + return prev +} + +func (s *hookedStateDB) SelfDestruct6780(address common.Address) (*big.Int, bool) { + prev, changed := s.inner.SelfDestruct6780(address) + if prev.Sign() != 0 && changed { + if s.hooks.OnBalanceChange != nil { + s.hooks.OnBalanceChange(address, prev, new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + } + return prev, changed +} + +func (s *hookedStateDB) AddLog(log *types.Log) { + // The inner will modify the log (add fields), so invoke that first + s.inner.AddLog(log) + if s.hooks.OnLog != nil { + s.hooks.OnLog(log) + } +} + +func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { + defer s.inner.Finalise(deleteEmptyObjects) + if s.hooks.OnBalanceChange == nil { + return + } + for addr := range s.inner.journal.dirties { + obj := s.inner.stateObjects[addr] + if obj != nil && obj.selfDestructed { + // If ether was sent to account post-selfdestruct it is burnt. + if bal := obj.Balance(); bal.Sign() != 0 { + s.hooks.OnBalanceChange(addr, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + } + } + } +} + +func (s *hookedStateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + return s.inner.IntermediateRoot(deleteEmptyObjects) +} + +func (s *hookedStateDB) UpdateTRC21Fee(newBalance map[common.Address]*big.Int, totalFeeUsed *big.Int) { + s.inner.UpdateTRC21Fee(newBalance, totalFeeUsed) +} + +func (s *hookedStateDB) GetTotalMinted() common.Hash { + return s.inner.GetTotalMinted() +} + +func (s *hookedStateDB) GetLastEpochNum() common.Hash { + return s.inner.GetLastEpochNum() +} + +func (s *hookedStateDB) PutTotalMinted(value common.Hash) { + s.inner.PutTotalMinted(value) +} + +func (s *hookedStateDB) PutLastEpochNum(value common.Hash) { + s.inner.PutLastEpochNum(value) +} + +func (s *hookedStateDB) IncrementMintedRecordNonce() { + s.inner.IncrementMintedRecordNonce() +} diff --git a/core/state/statedb_hooked_test.go b/core/state/statedb_hooked_test.go new file mode 100644 index 000000000000..3efa1d667049 --- /dev/null +++ b/core/state/statedb_hooked_test.go @@ -0,0 +1,129 @@ +// Copyright 2024 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 . + +package state + +import ( + "fmt" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/tracing" + "github.com/XinFinOrg/XDPoSChain/core/types" +) + +// This method tests that the 'burn' from sending-to-selfdestructed accounts +// is accounted for. +// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct ) +func TestBurn(t *testing.T) { + // Note: burn can happen even after EIP-6780, if within one single transaction, + // the following occur: + // 1. contract B creates contract A + // 2. contract A is destructed + // 3. constract B sends ether to A + + var burned = new(big.Int) + s, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + hooked := NewHookedState(s, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + if reason == tracing.BalanceDecreaseSelfdestructBurn { + burned.Add(burned, prev) + } + }, + }) + createAndDestroy := func(addr common.Address) { + hooked.AddBalance(addr, big.NewInt(100), tracing.BalanceChangeUnspecified) + hooked.CreateContract(addr) + hooked.SelfDestruct(addr) + // sanity-check that balance is now 0 + if have, want := hooked.GetBalance(addr), new(big.Int); have.Cmp(want) != 0 { + t.Fatalf("post-destruct balance wrong: have %v want %v", have, want) + } + } + addA := common.Address{0xaa} + addB := common.Address{0xbb} + addC := common.Address{0xcc} + + // Tx 1: create and destroy address A and B in one tx + createAndDestroy(addA) + createAndDestroy(addB) + hooked.AddBalance(addA, big.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.AddBalance(addB, big.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + // Tx 2: create and destroy address C, then commit + createAndDestroy(addC) + hooked.AddBalance(addC, big.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + s.Commit(false) + if have, want := burned, big.NewInt(600); have.Cmp(want) != 0 { + t.Fatalf("burn-count wrong, have %v want %v", have, want) + } +} + +// TestHooks is a basic sanity-check of all hooks +func TestHooks(t *testing.T) { + inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + inner.SetTxContext(common.Hash{0x11}, 100) // For the log + var result []string + var wants = []string{ + "0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)", + "0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)", + "0xaa00000000000000000000000000000000000000.nonce: 1336->1337", + "0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022", + "log 100", + } + emitF := func(format string, a ...any) { + result = append(result, fmt.Sprintf(format, a...)) + } + sdb := NewHookedState(inner, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason) + }, + OnNonceChange: func(addr common.Address, prev, new uint64) { + emitF("%v.nonce: %v->%v", addr, prev, new) + }, + OnCodeChange: func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) { + emitF("%v.code: %#x (%v) ->%#x (%v)", addr, prevCode, prevCodeHash, code, codeHash) + }, + OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) { + emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new) + }, + OnLog: func(log *types.Log) { + emitF("log %v", log.TxIndex) + }, + }) + sdb.AddBalance(common.Address{0xaa}, big.NewInt(100), tracing.BalanceChangeUnspecified) + sdb.SubBalance(common.Address{0xaa}, big.NewInt(50), tracing.BalanceChangeTransfer) + sdb.SetNonce(common.Address{0xaa}, 1337) + sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11")) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x02")) + sdb.AddLog(&types.Log{ + Address: common.Address{0xbb}, + }) + for i, want := range wants { + if have := result[i]; have != want { + t.Fatalf("error event %d, have\n%v\nwant%v\n", i, have, want) + } + } +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 24854dd7ee6f..160cf6c4854c 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -155,7 +155,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(big.NewInt(int64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(int64(i))) orig.updateStateObject(obj) } orig.Finalise(false) @@ -172,9 +172,9 @@ func TestCopy(t *testing.T) { copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(big.NewInt(2*int64(i)), tracing.BalanceChangeUnspecified) - copyObj.AddBalance(big.NewInt(3*int64(i)), tracing.BalanceChangeUnspecified) - ccopyObj.AddBalance(big.NewInt(4*int64(i)), tracing.BalanceChangeUnspecified) + origObj.AddBalance(big.NewInt(2 * int64(i))) + copyObj.AddBalance(big.NewInt(3 * int64(i))) + ccopyObj.AddBalance(big.NewInt(4 * int64(i))) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -298,6 +298,27 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { s.CreateAccount(addr) }, }, + { + name: "CreateContract", + fn: func(a testAction, s *StateDB) { + if !s.Exist(addr) { + s.CreateAccount(addr) + } + contractHash := s.GetCodeHash(addr) + emptyCode := contractHash == (common.Hash{}) || contractHash == types.EmptyCodeHash + storageRoot := s.GetStorageRoot(addr) + emptyStorage := storageRoot == (common.Hash{}) || storageRoot == types.EmptyRootHash + if s.GetNonce(addr) == 0 && emptyCode && emptyStorage { + s.CreateContract(addr) + // We also set some code here, to prevent the + // CreateContract action from being performed twice in a row, + // which would cause a difference in state when unrolling + // the journal. (CreateContact assumes created was false prior to + // invocation, and the journal rollback sets it to false). + s.SetCode(addr, []byte{1}) + } + }, + }, { name: "SelfDestruct", fn: func(a testAction, s *StateDB) { diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 0b7c7a5de267..34d8ea557323 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -23,7 +23,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" @@ -52,7 +51,7 @@ func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) { obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(big.NewInt(11*int64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(11 * int64(i))) acc.balance = big.NewInt(11 * int64(i)) obj.SetNonce(uint64(42 * i)) diff --git a/core/state_processor.go b/core/state_processor.go index 7b6892231b96..1238f7aa5ce9 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -76,9 +76,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) + + var tracingStateDB = vm.StateDB(statedb) + if hooks := cfg.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + } + // Mutate the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { - misc.ApplyDAOHardFork(statedb) + misc.ApplyDAOHardFork(tracingStateDB) } if common.TIPSigning.Cmp(blockNumber) == 0 { statedb.DeleteAddress(common.BlockSignersBinary) @@ -87,10 +93,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra InitSignerInTransactions(p.config, header, block.Transactions()) balanceUpdated := map[common.Address]*big.Int{} totalFeeUsed := big.NewInt(0) + + // Apply pre-execution system calls. blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) signer := types.MakeSigner(p.config, blockNumber) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { // check black-list txs after hf @@ -144,9 +153,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) } } - statedb.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) + tracingStateDB.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, parentState, block.Transactions(), block.Uncles(), receipts) + p.engine.Finalize(p.bc, header, tracingStateDB, parentState, block.Transactions(), block.Uncles(), receipts) + return receipts, allLogs, *usedGas, nil } @@ -161,9 +172,15 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) + + var tracingStateDB = vm.StateDB(statedb) + if hooks := cfg.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + } + // Mutate the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { - misc.ApplyDAOHardFork(statedb) + misc.ApplyDAOHardFork(tracingStateDB) } if common.TIPSigning.Cmp(blockNumber) == 0 { statedb.DeleteAddress(common.BlockSignersBinary) @@ -179,10 +196,13 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated if cBlock.stop { return nil, nil, 0, ErrStopPreparingBlock } + + // Apply pre-execution system calls. blockContext := NewEVMBlockContext(header, p.bc, nil) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, tracingStateDB, tradingState, p.config, cfg) signer := types.MakeSigner(p.config, blockNumber) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) + // Iterate over and process the individual transactions receipts = make([]*types.Receipt, block.Transactions().Len()) for i, tx := range block.Transactions() { @@ -222,6 +242,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return nil, nil, 0, err } statedb.SetTxContext(tx.Hash(), i) + receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner) if err != nil { return nil, nil, 0, err @@ -238,9 +259,10 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) } } - statedb.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) + tracingStateDB.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, parentState, block.Transactions(), block.Uncles(), receipts) + p.engine.Finalize(p.bc, header, tracingStateDB, parentState, block.Transactions(), block.Uncles(), receipts) return receipts, allLogs, *usedGas, nil } @@ -251,35 +273,36 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo to := tx.To() if to != nil { if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) { - return ApplySignTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + return ApplySignTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } if *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(blockNumber) { - return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } if *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(blockNumber) { - return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } } if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(blockNumber) { - return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(blockNumber) { - return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { - evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - if evm.Config.Tracer.OnTxEnd != nil { - defer func() { - evm.Config.Tracer.OnTxEnd(receipt, err) - }() + var tracingStateDB = vm.StateDB(statedb) + if hooks := evm.Config.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + if hooks.OnTxEnd != nil { + defer func() { hooks.OnTxEnd(receipt, err) }() } } + // Create a new context to be used in the EVM environment txContext := NewEVMTxContext(msg) - - // Update the evm with the new transaction context. - evm.Reset(txContext, statedb) + evm.Reset(txContext, tracingStateDB) // Bypass blacklist address maxBlockNumber := new(big.Int).SetInt64(9147459) @@ -434,7 +457,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo // Update the state with pending changes. var root []byte if config.IsByzantium(blockNumber) { - statedb.Finalise(true) + tracingStateDB.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } @@ -509,11 +532,22 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, balanceFee, coinbaseOwner) } -func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, bool, error) { +func ApplySignTransaction(msg *Message, config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) { + var tracingStateDB = vm.StateDB(statedb) + if hooks := evm.Config.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + if hooks.OnTxEnd != nil { + defer func() { hooks.OnTxEnd(receipt, err) }() + } + } + // Update the state with pending changes var root []byte if config.IsByzantium(blockNumber) { - statedb.Finalise(true) + tracingStateDB.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } @@ -530,7 +564,7 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, bl statedb.SetNonce(from, nonce+1) // 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 wether the root touch-delete accounts. - receipt := types.NewReceipt(root, false, *usedGas) + receipt = types.NewReceipt(root, false, *usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = 0 // if the transaction created a contract, store the creation address in the receipt. @@ -547,17 +581,28 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, bl return receipt, 0, false, nil } -func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, bool, error) { +func ApplyEmptyTransaction(msg *Message, config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) { + var tracingStateDB = vm.StateDB(statedb) + if hooks := evm.Config.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + if hooks.OnTxEnd != nil { + defer func() { hooks.OnTxEnd(receipt, err) }() + } + } + // Update the state with pending changes var root []byte if config.IsByzantium(blockNumber) { - statedb.Finalise(true) + tracingStateDB.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } // 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 wether the root touch-delete accounts. - receipt := types.NewReceipt(root, false, *usedGas) + receipt = types.NewReceipt(root, false, *usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = 0 // if the transaction created a contract, store the creation address in the receipt. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 9d9a6e9ea6c3..c6350389aef4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -860,7 +860,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) - interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) + interpreter.evm.StateDB.SelfDestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) diff --git a/core/vm/interface.go b/core/vm/interface.go index 210992239925..d06184477b88 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -29,8 +29,8 @@ import ( type StateDB interface { CreateAccount(common.Address) - SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason) - AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason) + SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason) *big.Int + AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason) *big.Int GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 @@ -47,16 +47,21 @@ type StateDB interface { GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) + SetState(common.Address, common.Hash, common.Hash) common.Hash GetStorageRoot(addr common.Address) common.Hash GetTransientState(addr common.Address, key common.Hash) common.Hash SetTransientState(addr common.Address, key, value common.Hash) - SelfDestruct(common.Address) + SelfDestruct(common.Address) *big.Int HasSelfDestructed(common.Address) bool - Selfdestruct6780(common.Address) + // SelfDestruct6780 is post-EIP6780 selfdestruct, which means that it's a + // send-all-to-beneficiary, unless the contract was created in this same + // transaction, in which case it will be destructed. + // This method returns the prior balance, along with a boolean which is + // true iff the object was indeed destructed. + SelfDestruct6780(common.Address) (*big.Int, bool) // Exist reports whether the given account exists in state. // Notably this should also return true for self-destructed accounts. @@ -80,6 +85,19 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) + + // Finalise must be invoked at the end of a transaction + Finalise(bool) + + IntermediateRoot(deleteEmptyObjects bool) common.Hash + + UpdateTRC21Fee(newBalance map[common.Address]*big.Int, totalFeeUsed *big.Int) + + GetTotalMinted() common.Hash + GetLastEpochNum() common.Hash + PutTotalMinted(value common.Hash) + PutLastEpochNum(value common.Hash) + IncrementMintedRecordNonce() } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index 972826b3971f..4b19bdf936fc 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -18,6 +18,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" @@ -255,7 +256,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } // Hook calculates reward for masternodes - adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { + adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { number := header.Number.Uint64() rCheckpoint := chain.Config().XDPoS.RewardCheckpoint foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index d1f8ac16592c..9808c9aeeeee 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -16,6 +16,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" @@ -264,7 +265,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } // Hook calculates reward for masternodes - adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { + adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { number := header.Number.Uint64() foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr if foundationWalletAddr == (common.Address{}) { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index e85d43f522bd..28d59a493e25 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -881,7 +881,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor } // The actual TxContext will be created as part of ApplyTransactionWithEVM. vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice}, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) - statedb.SetLogger(tracer.Hooks) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index b757f8abd1a6..14dd6ae533f5 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -126,20 +127,22 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { GasLimit: uint64(test.Context.GasLimit), BaseFee: test.Genesis.BaseFee, } - state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) + st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) ) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - - state.SetLogger(tracer.Hooks) + logState := vm.StateDB(st) + if tracer.Hooks != nil { + logState = state.NewHookedState(st, tracer.Hooks) + } msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) if err != nil { @@ -348,7 +351,7 @@ func TestInternals(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - state := tests.MakePreState(rawdb.NewMemoryDatabase(), + st := tests.MakePreState(rawdb.NewMemoryDatabase(), types.GenesisAlloc{ to: types.Account{ Code: tc.code, @@ -357,7 +360,12 @@ func TestInternals(t *testing.T) { Balance: big.NewInt(500000000000000), }, }) - state.SetLogger(tc.tracer.Hooks) + + logState := vm.StateDB(st) + if hooks := tc.tracer.Hooks; hooks != nil { + logState = state.NewHookedState(st, hooks) + } + tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ To: &to, Value: big.NewInt(0), @@ -371,7 +379,7 @@ func TestInternals(t *testing.T) { Origin: origin, GasPrice: tx.GasPrice(), } - evm := vm.NewEVM(context, txContext, state, nil, config, vm.Config{Tracer: tc.tracer.Hooks}) + evm := vm.NewEVM(context, txContext, logState, nil, config, vm.Config{Tracer: tc.tracer.Hooks}) msg, err := core.TransactionToMessage(tx, signer, nil, nil, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 5f567732e66a..7e38f76e1da7 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -101,7 +101,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to create call tracer: %v", err) } - state.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index ee8368a0e549..c44220be2f76 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -109,18 +109,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - // msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee) - // if err != nil { - // t.Fatalf("failed to prepare transaction for tracing: %v", err) - // } - // evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer}) - // st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - // if _, err = st.TransitionDb(common.Address{}); err != nil { - // t.Fatalf("failed to execute transaction: %v", err) - // } - // // Retrieve the trace result and compare against the expected. - state.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 060e3ae5fd27..d3b11b2ce00d 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -48,9 +48,11 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } -func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } -func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } +func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash { + return common.Hash{} +} func TestStoreCapture(t *testing.T) { var ( diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c9e0c92f12ae..9756aa88a80c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1292,10 +1292,16 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s evm.SetPrecompiles(precompiles) } - return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) + res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp) + // If an internal state error occurred, let that have precedence. Otherwise, + // a "trie root missing" type of error will masquerade as e.g. "insufficient gas" + if err := state.Error(); err != nil { + return nil, err + } + return res, err } -func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { +func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { @@ -1305,9 +1311,6 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st // Execute the message. result, err := core.ApplyMessage(evm, msg, gp, common.Address{}) - if err := state.Error(); err != nil { - return nil, err - } // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 47785017aaa2..dd9d3353a3d9 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -177,7 +177,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, nil, sim.chainConfig, *vmConfig) ) - sim.state.SetLogger(tracer.Hooks()) + var tracingStateDB = vm.StateDB(sim.state) + if hooks := tracer.Hooks(); hooks != nil { + tracingStateDB = state.NewHookedState(sim.state, hooks) + } // It is possible to override precompiles with EVM bytecode, or // move them to another address. if precompiles != nil { @@ -195,8 +198,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(tx.Hash(), uint(i)) // EoA check is always skipped, even in validation mode. msg := call.ToMessage(sim.b, header.BaseFee, !sim.validate, true) - evm.Reset(core.NewEVMTxContext(msg), sim.state) - result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp) + evm.Reset(core.NewEVMTxContext(msg), tracingStateDB) + result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) return nil, nil, txErr @@ -204,7 +207,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, // Update the state with pending changes. var root []byte if sim.chainConfig.IsByzantium(blockContext.BlockNumber) { - sim.state.Finalise(true) + tracingStateDB.Finalise(true) } else { root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() } From 981ab7e3d5bebff3a72fedc30a94e17aee913d72 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Wed, 20 Nov 2024 08:40:21 +0100 Subject: [PATCH 08/10] core/vm/program: evm bytecode-building utility 30725 In many cases, there is a need to create somewhat nontrivial bytecode. A recent example is the verkle statetests, where we want a `CREATE2`- op to create a contract, which can then be invoked, and when invoked does a selfdestruct-to-self. It is overkill to go full solidity, but it is also a bit tricky do assemble this by concatenating bytes. This PR takes an approach that has been used in in goevmlab for several years. Using this utility, the case can be expressed as: ```golang // Some runtime code runtime := program.New().Ops(vm.ADDRESS, vm.SELFDESTRUCT).Bytecode() // A constructor returning the runtime code initcode := program.New().ReturnData(runtime).Bytecode() // A factory invoking the constructor outer := program.New().Create2AndCall(initcode, nil).Bytecode() ``` We have a lot of places in the codebase where we concatenate bytes, cast from `vm.OpCode` . By taking tihs approach instead, thos places can be made a bit more maintainable/robust. --- core/vm/program/program.go | 430 ++++++++++++++++++++++++++++++++ core/vm/program/program_test.go | 311 +++++++++++++++++++++++ core/vm/program/readme.md | 30 +++ core/vm/runtime/runtime_test.go | 309 ++++++++--------------- 4 files changed, 878 insertions(+), 202 deletions(-) create mode 100644 core/vm/program/program.go create mode 100644 core/vm/program/program_test.go create mode 100644 core/vm/program/readme.md diff --git a/core/vm/program/program.go b/core/vm/program/program.go new file mode 100644 index 000000000000..8c32a3056dbf --- /dev/null +++ b/core/vm/program/program.go @@ -0,0 +1,430 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The 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. +// +// This 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 goevmlab library. If not, see . + +// package program is a utility to create EVM bytecode for testing, but _not_ for production. As such: +// +// - There are not package guarantees. We might iterate heavily on this package, and do backwards-incompatible changes without warning +// - There are no quality-guarantees. These utilities may produce evm-code that is non-functional. YMMV. +// - There are no stability-guarantees. The utility will `panic` if the inputs do not align / make sense. +package program + +import ( + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/holiman/uint256" +) + +// Program is a simple bytecode container. It can be used to construct +// simple EVM programs. Errors during construction of a Program typically +// cause panics: so avoid using these programs in production settings or on +// untrusted input. +// This package is mainly meant to aid in testing. This is not a production +// -level "compiler". +type Program struct { + code []byte +} + +// New creates a new Program +func New() *Program { + return &Program{ + code: make([]byte, 0), + } +} + +// add adds the op to the code. +func (p *Program) add(op byte) *Program { + p.code = append(p.code, op) + return p +} + +// pushBig creates a PUSHX instruction and pushes the given val. +// - If the val is nil, it pushes zero +// - If the val is bigger than 32 bytes, it panics +func (p *Program) doPush(val *uint256.Int) { + if val == nil { + val = new(uint256.Int) + } + valBytes := val.Bytes() + if len(valBytes) == 0 { + valBytes = append(valBytes, 0) + } + bLen := len(valBytes) + p.add(byte(vm.PUSH1) - 1 + byte(bLen)) + p.Append(valBytes) +} + +// Append appends the given data to the code. +func (p *Program) Append(data []byte) *Program { + p.code = append(p.code, data...) + return p +} + +// Bytes returns the Program bytecode. OBS: This is not a copy. +func (p *Program) Bytes() []byte { + return p.code +} + +// SetBytes sets the Program bytecode. The combination of Bytes and SetBytes means +// that external callers can implement missing functionality: +// +// ... +// prog.Push(1) +// code := prog.Bytes() +// manipulate(code) +// prog.SetBytes(code) +func (p *Program) SetBytes(code []byte) { + p.code = code +} + +// Hex returns the Program bytecode as a hex string. +func (p *Program) Hex() string { + return fmt.Sprintf("%02x", p.Bytes()) +} + +// Op appends the given opcode(s). +func (p *Program) Op(ops ...vm.OpCode) *Program { + for _, op := range ops { + p.add(byte(op)) + } + return p +} + +// Push creates a PUSHX instruction with the data provided. If zero is being pushed, +// PUSH0 will be avoided in favour of [PUSH1 0], to ensure backwards compatibility. +func (p *Program) Push(val any) *Program { + switch v := val.(type) { + case int: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case uint64: + p.doPush(new(uint256.Int).SetUint64(v)) + case uint32: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case uint16: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case *big.Int: + p.doPush(uint256.MustFromBig(v)) + case *uint256.Int: + p.doPush(v) + case uint256.Int: + p.doPush(&v) + case []byte: + p.doPush(new(uint256.Int).SetBytes(v)) + case byte: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case interface{ Bytes() []byte }: + // Here, we jump through some hoops in order to avoid depending on + // go-ethereum types.Address and common.Hash, and instead use the + // interface. This works on both values and pointers! + p.doPush(new(uint256.Int).SetBytes(v.Bytes())) + case nil: + p.doPush(nil) + default: + panic(fmt.Sprintf("unsupported type %T", v)) + } + return p +} + +// Push0 implements PUSH0 (0x5f). +func (p *Program) Push0() *Program { + return p.Op(vm.PUSH0) +} + +// ExtcodeCopy performs an extcodecopy invocation. +func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Program { + p.Push(length) + p.Push(codeOffset) + p.Push(memOffset) + p.Push(address) + return p.Op(vm.EXTCODECOPY) +} + +// Call is a convenience function to make a call. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) Call(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize && value == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset).Push(value) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.CALL) +} + +// DelegateCall is a convenience function to make a delegatecall. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) DelegateCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.DELEGATECALL) +} + +// StaticCall is a convenience function to make a staticcall. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) StaticCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.STATICCALL) +} + +// StaticCall is a convenience function to make a callcode. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) CallCode(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(value) + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.CALLCODE) +} + +// Label returns the PC (of the next instruction). +func (p *Program) Label() uint64 { + return uint64(len(p.code)) +} + +// Jumpdest adds a JUMPDEST op, and returns the PC of that instruction. +func (p *Program) Jumpdest() (*Program, uint64) { + here := p.Label() + p.Op(vm.JUMPDEST) + return p, here +} + +// Jump pushes the destination and adds a JUMP. +func (p *Program) Jump(loc any) *Program { + p.Push(loc) + p.Op(vm.JUMP) + return p +} + +// JumpIf implements JUMPI. +func (p *Program) JumpIf(loc any, condition any) *Program { + p.Push(condition) + p.Push(loc) + p.Op(vm.JUMPI) + return p +} + +// Size returns the current size of the bytecode. +func (p *Program) Size() int { + return len(p.code) +} + +// InputAddressToStack stores the input (calldata) to memory as address (20 bytes). +func (p *Program) InputAddressToStack(inputOffset uint32) *Program { + p.Push(inputOffset) + p.Op(vm.CALLDATALOAD) // Loads [n -> n + 32] of input data to stack top + mask, _ := big.NewInt(0).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) + p.Push(mask) // turn into address + return p.Op(vm.AND) +} + +// MStore stores the provided data (into the memory area starting at memStart). +func (p *Program) Mstore(data []byte, memStart uint32) *Program { + var idx = 0 + // We need to store it in chunks of 32 bytes + for ; idx+32 <= len(data); idx += 32 { + chunk := data[idx : idx+32] + // push the value + p.Push(chunk) + // push the memory index + p.Push(uint32(idx) + memStart) + p.Op(vm.MSTORE) + } + // Remainders become stored using MSTORE8 + for ; idx < len(data); idx++ { + b := data[idx] + // push the byte + p.Push(b) + p.Push(uint32(idx) + memStart) + p.Op(vm.MSTORE8) + } + return p +} + +// MstoreSmall stores the provided data, which must be smaller than 32 bytes, +// into the memory area starting at memStart. +// The data will be LHS zero-added to align on 32 bytes. +// For example, providing data 0x1122, it will do a PUSH2: +// PUSH2 0x1122, resulting in +// stack: 0x0000000000000000000000000000000000000000000000000000000000001122 +// followed by MSTORE(0,0) +// And thus, the resulting memory will be +// [ 0000000000000000000000000000000000000000000000000000000000001122 ] +func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program { + if len(data) > 32 { + // For larger sizes, use Mstore instead. + panic("only <=32 byte data size supported") + } + if len(data) == 0 { + // Storing 0-length data smells of an error somewhere. + panic("data is zero length") + } + // push the value + p.Push(data) + // push the memory index + p.Push(memStart) + p.Op(vm.MSTORE) + return p +} + +// MemToStorage copies the given memory area into SSTORE slots, +// It expects data to be aligned to 32 byte, and does not zero out +// remainders if some data is not +// I.e, if given a 1-byte area, it will still copy the full 32 bytes to storage. +func (p *Program) MemToStorage(memStart, memSize, startSlot int) *Program { + // We need to store it in chunks of 32 bytes + for idx := memStart; idx < (memStart + memSize); idx += 32 { + dataStart := idx + // Mload the chunk + p.Push(dataStart) + p.Op(vm.MLOAD) + // Value is now on stack, + p.Push(startSlot) + p.Op(vm.SSTORE) + startSlot++ + } + return p +} + +// ReturnViaCodeCopy utilises CODECOPY to place the given data in the bytecode of +// p, loads into memory (offset 0) and returns the code. +// This is a typical "constructor". +// Note: since all indexing is calculated immediately, the preceding bytecode +// must not be expanded or shortened. +func (p *Program) ReturnViaCodeCopy(data []byte) *Program { + p.Push(len(data)) + // For convenience, we'll use PUSH2 for the offset. Then we know we can always + // fit, since code is limited to 0xc000 + p.Op(vm.PUSH2) + offsetPos := p.Size() // Need to update this position later on + p.Append([]byte{0, 0}) // Offset of the code to be copied + p.Push(0) // Offset in memory (destination) + p.Op(vm.CODECOPY) // Copy from code[offset:offset+len] to memory[0:] + p.Return(0, len(data)) // Return memory[0:len] + offset := p.Size() + p.Append(data) // And add the data + + // Now, go back and fix the offset + p.code[offsetPos] = byte(offset >> 8) + p.code[offsetPos+1] = byte(offset) + return p +} + +// Sstore stores the given byte array to the given slot. +// OBS! Does not verify that the value indeed fits into 32 bytes. +// If it does not, it will panic later on via doPush. +func (p *Program) Sstore(slot any, value any) *Program { + p.Push(value) + p.Push(slot) + return p.Op(vm.SSTORE) +} + +// Tstore stores the given byte array to the given t-slot. +// OBS! Does not verify that the value indeed fits into 32 bytes. +// If it does not, it will panic later on via doPush. +func (p *Program) Tstore(slot any, value any) *Program { + p.Push(value) + p.Push(slot) + return p.Op(vm.TSTORE) +} + +// Return implements RETURN +func (p *Program) Return(offset, len int) *Program { + p.Push(len) + p.Push(offset) + return p.Op(vm.RETURN) +} + +// ReturnData loads the given data into memory, and does a return with it +func (p *Program) ReturnData(data []byte) *Program { + p.Mstore(data, 0) + return p.Return(0, len(data)) +} + +// Create2 uses create2 to construct a contract with the given bytecode. +// This operation leaves either '0' or address on the stack. +func (p *Program) Create2(code []byte, salt any) *Program { + var ( + value = 0 + offset = 0 + size = len(code) + ) + // Load the code into mem + p.Mstore(code, 0) + // Create it + return p.Push(salt). + Push(size). + Push(offset). + Push(value). + Op(vm.CREATE2) + // On the stack now, is either + // - zero: in case of failure, OR + // - address: in case of success +} + +// Create2ThenCall calls create2 with the given initcode and salt, and then calls +// into the created contract (or calls into zero, if the creation failed). +func (p *Program) Create2ThenCall(code []byte, salt any) *Program { + p.Create2(code, salt) + // If there happen to be a zero on the stack, it doesn't matter, we're + // not sending any value anyway + p.Push(0).Push(0) // mem out + p.Push(0).Push(0) // mem in + p.Push(0) // value + p.Op(vm.DUP6) // address + p.Op(vm.GAS) + p.Op(vm.CALL) + p.Op(vm.POP) // pop the retval + return p.Op(vm.POP) // pop the address +} + +// Selfdestruct pushes beneficiary and invokes selfdestruct. +func (p *Program) Selfdestruct(beneficiary any) *Program { + p.Push(beneficiary) + return p.Op(vm.SELFDESTRUCT) +} diff --git a/core/vm/program/program_test.go b/core/vm/program/program_test.go new file mode 100644 index 000000000000..d8f54d4acb80 --- /dev/null +++ b/core/vm/program/program_test.go @@ -0,0 +1,311 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The 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. +// +// This 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 goevmlab library. If not, see . + +package program + +import ( + "bytes" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/holiman/uint256" +) + +func TestPush(t *testing.T) { + tests := []struct { + input interface{} + expected string + }{ + // native ints + {0, "6000"}, + {0xfff, "610fff"}, + {nil, "6000"}, + {uint8(1), "6001"}, + {uint16(1), "6001"}, + {uint32(1), "6001"}, + {uint64(1), "6001"}, + // bigints + {big.NewInt(0), "6000"}, + {big.NewInt(1), "6001"}, + {big.NewInt(0xfff), "610fff"}, + // uint256 + {uint256.NewInt(1), "6001"}, + {uint256.Int{1, 0, 0, 0}, "6001"}, + // Addresses + {common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}, + {&common.Address{}, "6000"}, + } + for i, tc := range tests { + have := New().Push(tc.input).Hex() + if have != tc.expected { + t.Errorf("test %d: got %v expected %v", i, have, tc.expected) + } + } +} + +func TestCall(t *testing.T) { + { // Nil gas + have := New().Call(nil, common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex() + want := "600460036002600160016113375af1" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // Non nil gas + have := New().Call(uint256.NewInt(0xffff), common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex() + want := "6004600360026001600161133761fffff1" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestMstore(t *testing.T) { + { + have := New().Mstore(common.FromHex("0xaabb"), 0).Hex() + want := "60aa60005360bb600153" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // store at offset + have := New().Mstore(common.FromHex("0xaabb"), 3).Hex() + want := "60aa60035360bb600453" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // 34 bytes + data := common.FromHex("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + "FFFF") + + have := New().Mstore(data, 0).Hex() + want := "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260ff60205360ff602153" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestMemToStorage(t *testing.T) { + have := New().MemToStorage(0, 33, 1).Hex() + want := "600051600155602051600255" + if have != want { + t.Errorf("have %v want %v", have, want) + } +} + +func TestSstore(t *testing.T) { + have := New().Sstore(0x1337, []byte("1234")).Hex() + want := "633132333461133755" + if have != want { + t.Errorf("have %v want %v", have, want) + } +} + +func TestReturnData(t *testing.T) { + { + have := New().ReturnData([]byte{0xFF}).Hex() + want := "60ff60005360016000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { + // 32 bytes + data := common.FromHex("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + have := New().ReturnData(data).Hex() + want := "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // ReturnViaCodeCopy + data := common.FromHex("0x6001") + have := New().Append([]byte{0x5b, 0x5b, 0x5b}).ReturnViaCodeCopy(data).Hex() + want := "5b5b5b600261001060003960026000f36001" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // ReturnViaCodeCopy larger code + data := common.FromHex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3") + have := New().Append([]byte{0x5b, 0x5b, 0x5b}).ReturnViaCodeCopy(data).Hex() + want := "5b5b5b602961001060003960296000f37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestCreateAndCall(t *testing.T) { + // A constructor that stores a slot + ctor := New().Sstore(0, big.NewInt(5)) + + // A runtime bytecode which reads the slot and returns + deployed := New() + deployed.Push(0).Op(vm.SLOAD) // [value] in stack + deployed.Push(0) // [value, 0] + deployed.Op(vm.MSTORE) + deployed.Return(0, 32) + + // Pack them + ctor.ReturnData(deployed.Bytes()) + // Verify constructor + runtime code + { + want := "6005600055606060005360006001536054600253606060035360006004536052600553606060065360206007536060600853600060095360f3600a53600b6000f3" + if got := ctor.Hex(); got != want { + t.Fatalf("1: got %v expected %v", got, want) + } + } +} + +func TestCreate2Call(t *testing.T) { + // Some runtime code + runtime := New().Op(vm.ADDRESS, vm.SELFDESTRUCT).Bytes() + want := common.FromHex("0x30ff") + if !bytes.Equal(want, runtime) { + t.Fatalf("runtime code error\nwant: %x\nhave: %x\n", want, runtime) + } + // A constructor returning the runtime code + initcode := New().ReturnData(runtime).Bytes() + want = common.FromHex("603060005360ff60015360026000f3") + if !bytes.Equal(want, initcode) { + t.Fatalf("initcode error\nwant: %x\nhave: %x\n", want, initcode) + } + // A factory invoking the constructor + outer := New().Create2ThenCall(initcode, nil).Bytes() + want = common.FromHex("60606000536030600153606060025360006003536053600453606060055360ff6006536060600753600160085360536009536060600a536002600b536060600c536000600d5360f3600e536000600f60006000f560006000600060006000855af15050") + if !bytes.Equal(want, outer) { + t.Fatalf("factory error\nwant: %x\nhave: %x\n", want, outer) + } +} + +func TestGenerator(t *testing.T) { + for i, tc := range []struct { + want []byte + haveFn func() []byte + }{ + { // CREATE + want: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // length, offset, value + byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE), + byte(vm.POP), + }, + haveFn: func() []byte { + initcode := New().Return(0, 0).Bytes() + return New().MstoreSmall(initcode, 0). + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE). + Op(vm.POP).Bytes() + }, + }, + { // CREATE2 + want: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // salt, length, offset, value + byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE2), + byte(vm.POP), + }, + haveFn: func() []byte { + initcode := New().Return(0, 0).Bytes() + return New().MstoreSmall(initcode, 0). + Push(1). // salt + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE2). + Op(vm.POP).Bytes() + }, + }, + { // CALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.DUP1), // value + byte(vm.PUSH1), 0xbb, //address + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().Call(nil, 0xbb, 0, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // CALLCODE + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0, // value + byte(vm.PUSH1), 0xcc, //address + byte(vm.GAS), // gas + byte(vm.CALLCODE), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().CallCode(nil, 0xcc, 0, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // STATICCALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xdd, //address + byte(vm.GAS), // gas + byte(vm.STATICCALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().StaticCall(nil, 0xdd, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // DELEGATECALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xee, //address + byte(vm.GAS), // gas + byte(vm.DELEGATECALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().DelegateCall(nil, 0xee, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + } { + if have := tc.haveFn(); !bytes.Equal(have, tc.want) { + t.Fatalf("test %d error\nhave: %x\nwant: %x\n", i, have, tc.want) + } + } +} diff --git a/core/vm/program/readme.md b/core/vm/program/readme.md new file mode 100644 index 000000000000..0e4a54d8f181 --- /dev/null +++ b/core/vm/program/readme.md @@ -0,0 +1,30 @@ +### What is this + +In many cases, we have a need to create somewhat nontrivial bytecode, for testing various +quirks related to state transition or evm execution. + +For example, we want to have a `CREATE2`- op create a contract, which is then invoked, and when invoked does a selfdestruct-to-self. + +It is overkill to go full solidity, but it is also a bit tricky do assemble this by concatenating bytes. + +This utility takes an approach from [goevmlab](https://github.com/holiman/goevmlab/) where it has been used for several years, +a go-lang utility to assemble evm bytecode. + +Using this utility, the case above can be expressed as: +```golang + // Some runtime code + runtime := program.New().Ops(vm.ADDRESS, vm.SELFDESTRUCT).Bytecode() + // A constructor returning the runtime code + initcode := program.New().ReturnData(runtime).Bytecode() + // A factory invoking the constructor + outer := program.New().Create2AndCall(initcode, nil).Bytecode() +``` + +### Warning + +This package is a utility for testing, _not_ for production. As such: + +- There are not package guarantees. We might iterate heavily on this package, and do backwards-incompatible changes without warning +- There are no quality-guarantees. These utilities may produce evm-code that is non-functional. YMMV. +- There are no stability-guarantees. The utility will `panic` if the inputs do not align / make sense. + diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 13e42af7a2bd..9d70376074ea 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/core/vm/program" "github.com/XinFinOrg/XDPoSChain/eth/tracers" "github.com/XinFinOrg/XDPoSChain/eth/tracers/logger" "github.com/XinFinOrg/XDPoSChain/params" @@ -331,16 +332,21 @@ func TestBlockhash(t *testing.T) { // benchmarkNonModifyingCode benchmarks code, but if the code modifies the // state, this should not be used, since it does not reset the state between runs. -func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) { +func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode string, b *testing.B) { cfg := new(Config) setDefaults(cfg) - cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase())) + cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) cfg.GasLimit = gas - var ( - destination = common.BytesToAddress([]byte("contract")) - vmenv = NewEnv(cfg) - sender = vm.AccountRef(cfg.Origin) - ) + if len(tracerCode) > 0 { + tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil, cfg.ChainConfig) + if err != nil { + b.Fatal(err) + } + cfg.EVMConfig = vm.Config{ + Tracer: tracer.Hooks, + } + } + destination := common.BytesToAddress([]byte("contract")) cfg.State.CreateAccount(destination) eoa := common.HexToAddress("E0") { @@ -360,12 +366,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing. //cfg.State.CreateAccount(cfg.Origin) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(destination, code) - vmenv.Call(sender, destination, nil, gas, cfg.Value) + Call(destination, nil, cfg) b.Run(name, func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmenv.Call(sender, destination, nil, gas, cfg.Value) + Call(destination, nil, cfg) } }) } @@ -373,113 +379,61 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing. // BenchmarkSimpleLoop test a pretty simple loop which loops until OOG // 55 ms func BenchmarkSimpleLoop(b *testing.B) { - staticCallIdentity := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - byte(vm.STATICCALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callIdentity := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callInexistant := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0xff, // address of existing contract - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callEOA := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0xE0, // address of EOA - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - loopingCode := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - - byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callRevertingContractWithInput := []byte{ - byte(vm.JUMPDEST), // - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.PUSH1), 0x20, // in size - byte(vm.PUSH1), 0x00, // in offset - byte(vm.PUSH1), 0x00, // value - byte(vm.PUSH1), 0xEE, // address of reverting contract - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - //tracer := vm.NewJSONLogger(nil, os.Stdout) + p, lbl := program.New().Jumpdest() + // Call identity, and pop return value + staticCallIdentity := p. + StaticCall(nil, 0x4, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callIdentity := p. + Call(nil, 0x4, 0, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callInexistant := p. + Call(nil, 0xff, 0, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callEOA := p. + Call(nil, 0xE0, 0, 0, 0, 0, 0). // call addr of EOA + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + // Push as if we were making call, then pop it off again, and loop + loopingCode := p.Push(0). + Op(vm.DUP1, vm.DUP1, vm.DUP1). + Push(0x4). + Op(vm.GAS, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP). + Jump(lbl).Bytes() + + p, lbl = program.New().Jumpdest() + loopingCode2 := p. + Push(0x01020304).Push(uint64(0x0102030405)). + Op(vm.POP, vm.POP). + Op(vm.PUSH6).Append(make([]byte, 6)).Op(vm.JUMP). // Jumpdest zero expressed in 6 bytes + Jump(lbl).Bytes() + + p, lbl = program.New().Jumpdest() + callRevertingContractWithInput := p. + Call(nil, 0xee, 0, 0, 0x20, 0x0, 0x0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + //tracer := logger.NewJSONLogger(nil, os.Stdout) //Execute(loopingCode, nil, &Config{ // EVMConfig: vm.Config{ // Debug: true, // Tracer: tracer, // }}) // 100M gas - benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b) - benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b) - benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b) - benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b) - benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b) - benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", b) + benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", "", b) + benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", "", b) + benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", "", b) + benchmarkNonModifyingCode(100000000, loopingCode2, "loop2-100M", "", b) + benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", "", b) + benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", "", b) + benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", "", b) //benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b) //benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b) @@ -704,104 +658,49 @@ func TestRuntimeJSTracer(t *testing.T) { this.exits++; this.gasUsed = res.getGasUsed(); }}`} + initcode := program.New().Return(0, 0).Bytes() tests := []struct { code []byte // One result per tracer results []string }{ - { - // CREATE - code: []byte{ - // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) - byte(vm.PUSH5), - // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - // length, offset, value - byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, - byte(vm.CREATE), - byte(vm.POP), - }, + { // CREATE + code: program.New().MstoreSmall(initcode, 0). + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE). + Op(vm.POP).Bytes(), results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`}, }, - { - // CREATE2 - code: []byte{ - // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) - byte(vm.PUSH5), - // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - // salt, length, offset, value - byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, - byte(vm.CREATE2), - byte(vm.POP), - }, + { // CREATE2 + code: program.New().MstoreSmall(initcode, 0). + Push(1). // salt + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE2). + Op(vm.POP).Bytes(), results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`}, }, - { - // CALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xbb, //address - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), - }, + { // CALL + code: program.New().Call(nil, 0xbb, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`}, }, - { - // CALLCODE - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xcc, //address - byte(vm.GAS), // gas - byte(vm.CALLCODE), - byte(vm.POP), - }, + { // CALLCODE + code: program.New().CallCode(nil, 0xcc, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`}, }, - { - // STATICCALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xdd, //address - byte(vm.GAS), // gas - byte(vm.STATICCALL), - byte(vm.POP), - }, + { // STATICCALL + code: program.New().StaticCall(nil, 0xdd, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`}, }, - { - // DELEGATECALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xee, //address - byte(vm.GAS), // gas - byte(vm.DELEGATECALL), - byte(vm.POP), - }, + { // DELEGATECALL + code: program.New().DelegateCall(nil, 0xee, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`}, }, - { - // CALL self-destructing contract - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xff, //address - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), - }, + { // CALL self-destructing contract + code: program.New().Call(nil, 0xff, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"2,2,0,5003,12"`, `"2,2,0,5003,0"`}, }, } @@ -884,16 +783,22 @@ func TestJSTracerCreateTx(t *testing.T) { func BenchmarkTracerStepVsCallFrame(b *testing.B) { // Simply pushes and pops some values in a loop - code := []byte{ - byte(vm.JUMPDEST), - byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, - byte(vm.POP), - byte(vm.POP), - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } + p, lbl := program.New().Jumpdest() + code := p.Push(0).Push(0).Op(vm.POP, vm.POP).Jump(lbl).Bytes() + stepTracer := ` + { + step: function() {}, + fault: function() {}, + result: function() {}, + }` + callFrameTracer := ` + { + enter: function() {}, + exit: function() {}, + fault: function() {}, + result: function() {}, + }` - benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", b) - benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", b) + benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b) + benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b) } From c6749f22168278412daf9bebd31021a9f79546bd Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 24 Sep 2025 13:55:46 +0800 Subject: [PATCH 09/10] all: implement eip-7702 set code tx 30078 --- common/constants.all.go | 3 + common/constants.devnet.go | 8 +- common/constants.local.go | 8 +- common/constants.mainnet.go | 12 +- common/constants.testnet.go | 8 +- core/bench_test.go | 2 +- core/error.go | 22 +- core/state/journal.go | 7 + core/state/state_object.go | 11 +- core/state/statedb.go | 5 +- core/state/statedb_hooked.go | 11 +- core/state_processor_test.go | 93 +++++-- core/state_transition.go | 105 +++++++- core/txpool/txpool.go | 2 +- core/types/gen_authorization.go | 75 ++++++ core/types/receipt.go | 4 +- core/types/transaction.go | 18 +- core/types/transaction_marshalling.go | 96 +++++++- core/types/transaction_signing.go | 99 +++++++- core/types/tx_setcode.go | 226 ++++++++++++++++++ core/types/tx_setcode_test.go | 70 ++++++ core/vm/eips.go | 69 ++++++ core/vm/evm.go | 39 ++- core/vm/interface.go | 4 +- core/vm/interpreter.go | 2 + core/vm/jump_table.go | 7 + core/vm/operations_acl.go | 68 ++++++ core/vm/runtime/runtime_test.go | 81 +++++++ .../testdata/prestate_tracer/setcode_tx.json | 80 +++++++ eth/tracers/native/prestate.go | 9 + internal/ethapi/api.go | 57 +++-- internal/ethapi/transaction_args.go | 30 +++ params/config.go | 65 ++++- params/protocol_params.go | 5 +- tests/gen_stauthorization.go | 74 ++++++ tests/gen_sttransaction.go | 6 + tests/state_test_util.go | 38 +++ 37 files changed, 1410 insertions(+), 109 deletions(-) create mode 100644 core/types/gen_authorization.go create mode 100644 core/types/tx_setcode.go create mode 100644 core/types/tx_setcode_test.go create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json create mode 100644 tests/gen_stauthorization.go diff --git a/common/constants.all.go b/common/constants.all.go index 3400ccad6f8e..52d432aee953 100644 --- a/common/constants.all.go +++ b/common/constants.all.go @@ -64,6 +64,7 @@ type constant struct { tipEpochHalving *big.Int eip1559Block *big.Int cancunBlock *big.Int + pragueBlock *big.Int trc21IssuerSMC Address xdcxListingSMC Address @@ -98,6 +99,7 @@ var ( TIPXDCXReceiverDisable = MainnetConstant.tipXDCXReceiverDisable Eip1559Block = MainnetConstant.eip1559Block CancunBlock = MainnetConstant.cancunBlock + PragueBlock = MainnetConstant.pragueBlock TIPUpgradeReward = MainnetConstant.tipUpgradeReward TipUpgradePenalty = MainnetConstant.tipUpgradePenalty TIPEpochHalving = MainnetConstant.tipEpochHalving @@ -160,6 +162,7 @@ func CopyConstants(chainID uint64) { TIPXDCXReceiverDisable = c.tipXDCXReceiverDisable Eip1559Block = c.eip1559Block CancunBlock = c.cancunBlock + PragueBlock = c.pragueBlock TIPUpgradeReward = c.tipUpgradeReward TipUpgradePenalty = c.tipUpgradePenalty TIPEpochHalving = c.tipEpochHalving diff --git a/common/constants.devnet.go b/common/constants.devnet.go index c7133f6315e4..9fd3f4218671 100644 --- a/common/constants.devnet.go +++ b/common/constants.devnet.go @@ -1,6 +1,7 @@ package common import ( + "math" "math/big" ) @@ -28,9 +29,10 @@ var DevnetConstant = constant{ tipXDCXReceiverDisable: big.NewInt(0), eip1559Block: big.NewInt(32400), cancunBlock: big.NewInt(43200), - tipUpgradeReward: big.NewInt(9999999999), - tipUpgradePenalty: big.NewInt(9999999999), - tipEpochHalving: big.NewInt(9999999999), + pragueBlock: big.NewInt(math.MaxInt64), + tipUpgradeReward: big.NewInt(math.MaxInt64), + tipUpgradePenalty: big.NewInt(math.MaxInt64), + tipEpochHalving: big.NewInt(math.MaxInt64), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), xdcxListingSMC: HexToAddress("0xDE34dD0f536170993E8CFF639DdFfCF1A85D3E53"), diff --git a/common/constants.local.go b/common/constants.local.go index 291d2871de94..a5a5e804e4ee 100644 --- a/common/constants.local.go +++ b/common/constants.local.go @@ -1,6 +1,7 @@ package common import ( + "math" "math/big" ) @@ -28,9 +29,10 @@ var localConstant = constant{ tipXDCXReceiverDisable: big.NewInt(0), eip1559Block: big.NewInt(0), cancunBlock: big.NewInt(0), - tipUpgradeReward: big.NewInt(999999999), - tipUpgradePenalty: big.NewInt(999999999), - tipEpochHalving: big.NewInt(999999999), + pragueBlock: big.NewInt(math.MaxInt64), + tipUpgradeReward: big.NewInt(math.MaxInt64), + tipUpgradePenalty: big.NewInt(math.MaxInt64), + tipEpochHalving: big.NewInt(math.MaxInt64), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), xdcxListingSMC: HexToAddress("0xDE34dD0f536170993E8CFF639DdFfCF1A85D3E53"), diff --git a/common/constants.mainnet.go b/common/constants.mainnet.go index b4123fee709f..8498744d3cce 100644 --- a/common/constants.mainnet.go +++ b/common/constants.mainnet.go @@ -1,6 +1,7 @@ package common import ( + "math" "math/big" ) @@ -26,11 +27,12 @@ var MainnetConstant = constant{ TIPV2SwitchBlock: big.NewInt(80370000), // Target 2nd Oct 2024 tipXDCXMinerDisable: big.NewInt(80370000), // Target 2nd Oct 2024 tipXDCXReceiverDisable: big.NewInt(80370900), // Target 2nd Oct 2024, safer to release after disable miner - eip1559Block: big.NewInt(9999999999), - cancunBlock: big.NewInt(9999999999), - tipUpgradeReward: big.NewInt(9999999999), - tipUpgradePenalty: big.NewInt(9999999999), - tipEpochHalving: big.NewInt(9999999999), + eip1559Block: big.NewInt(math.MaxInt64), + cancunBlock: big.NewInt(math.MaxInt64), + pragueBlock: big.NewInt(math.MaxInt64), + tipUpgradeReward: big.NewInt(math.MaxInt64), + tipUpgradePenalty: big.NewInt(math.MaxInt64), + tipEpochHalving: big.NewInt(math.MaxInt64), trc21IssuerSMC: HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee"), xdcxListingSMC: HexToAddress("0xDE34dD0f536170993E8CFF639DdFfCF1A85D3E53"), diff --git a/common/constants.testnet.go b/common/constants.testnet.go index 055fd43494d1..b873419381d1 100644 --- a/common/constants.testnet.go +++ b/common/constants.testnet.go @@ -1,6 +1,7 @@ package common import ( + "math" "math/big" ) @@ -28,9 +29,10 @@ var TestnetConstant = constant{ tipXDCXReceiverDisable: big.NewInt(66825000), // Target 26 Aug 2024 eip1559Block: big.NewInt(71550000), // Target 14th Feb 2025 cancunBlock: big.NewInt(71551800), - tipUpgradeReward: big.NewInt(9999999999), - tipUpgradePenalty: big.NewInt(9999999999), - tipEpochHalving: big.NewInt(9999999999), + pragueBlock: big.NewInt(math.MaxInt64), + tipUpgradeReward: big.NewInt(math.MaxInt64), + tipUpgradePenalty: big.NewInt(math.MaxInt64), + tipEpochHalving: big.NewInt(math.MaxInt64), trc21IssuerSMC: HexToAddress("0x0E2C88753131CE01c7551B726b28BFD04e44003F"), xdcxListingSMC: HexToAddress("0x14B2Bf043b9c31827A472CE4F94294fE9a6277e0"), diff --git a/core/bench_test.go b/core/bench_test.go index 858b5c93e070..fe7d73a4c66b 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -84,7 +84,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false, false) + gas, _ := IntrinsicGas(data, nil, 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) } diff --git a/core/error.go b/core/error.go index 67bb75730e84..1e4c9a015c18 100644 --- a/core/error.go +++ b/core/error.go @@ -99,9 +99,25 @@ var ( // ErrSenderNoEOA is returned if the sender of a transaction is a contract. ErrSenderNoEOA = errors.New("sender not an eoa") - ErrNotXDPoS = errors.New("XDPoS not found in config") - - ErrNotFoundM1 = errors.New("list M1 not found ") + // -- XDPoS errors -- + ErrNotXDPoS = errors.New("XDPoS not found in config") + ErrNotFoundM1 = errors.New("list M1 not found ") ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2") + + // -- EIP-7702 errors -- + + // Message validation errors: + ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list") + ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract") +) + +// EIP-7702 state transition errors. +// Note these are just informational, and do not cause tx execution abort. +var ( + ErrAuthorizationWrongChainID = errors.New("EIP-7702 authorization chain ID mismatch") + ErrAuthorizationNonceOverflow = errors.New("EIP-7702 authorization nonce > 64 bit") + ErrAuthorizationInvalidSignature = errors.New("EIP-7702 authorization has invalid signature") + ErrAuthorizationDestinationHasCode = errors.New("EIP-7702 authorization destination is a contract") + ErrAuthorizationNonceMismatch = errors.New("EIP-7702 authorization nonce does not match current account nonce") ) diff --git a/core/state/journal.go b/core/state/journal.go index 1ffeef1ad0cd..400c3fdd5fda 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -89,6 +89,13 @@ func (j *journal) createContract(addr common.Address) { j.append(createContractChange{account: addr}) } +func (j *journal) setCode(address common.Address, prevCode []byte) { + j.append(codeChange{ + account: address, + prevCode: prevCode, + }) +} + type ( // Changes to the account trie. createObjectChange struct { diff --git a/core/state/state_object.go b/core/state/state_object.go index bc0ab83fb389..5c100f0e3c09 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -22,6 +22,7 @@ import ( "io" "maps" "math/big" + "slices" "time" "github.com/XinFinOrg/XDPoSChain/common" @@ -423,13 +424,11 @@ func (s *stateObject) CodeSize(db Database) int { return size } -func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { - prevCode := s.Code(s.db.db) - s.db.journal.append(codeChange{ - account: s.address, - prevCode: prevCode, - }) +func (s *stateObject) SetCode(codeHash common.Hash, code []byte) []byte { + prevCode := slices.Clone(s.code) + s.db.journal.setCode(s.address, prevCode) s.setCode(codeHash, code) + return prevCode } func (s *stateObject) setCode(codeHash common.Hash, code []byte) { diff --git a/core/state/statedb.go b/core/state/statedb.go index f575a171443b..943b5e4ae7a1 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -416,11 +416,12 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { } } -func (s *StateDB) SetCode(addr common.Address, code []byte) { +func (s *StateDB) SetCode(addr common.Address, code []byte) []byte { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetCode(crypto.Keccak256Hash(code), code) + return stateObject.SetCode(crypto.Keccak256Hash(code), code) } + return nil } func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash { diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 9cd358bc93cc..a31e5898e6d0 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -171,11 +171,16 @@ func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) { } } -func (s *hookedStateDB) SetCode(address common.Address, code []byte) { - s.inner.SetCode(address, code) +func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { + prev := s.inner.SetCode(address, code) if s.hooks.OnCodeChange != nil { - s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code) + prevHash := types.EmptyCodeHash + if len(prev) != 0 { + prevHash = crypto.Keccak256Hash(prev) + } + s.hooks.OnCodeChange(address, prevHash, prev, crypto.Keccak256Hash(code), code) } + return prev } func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { diff --git a/core/state_processor_test.go b/core/state_processor_test.go index d6eb7d6f3ff5..72f6a8d397fd 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/trie" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -50,15 +51,18 @@ func TestStateProcessorErrors(t *testing.T) { IstanbulBlock: big.NewInt(0), BerlinBlock: big.NewInt(0), LondonBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), Eip1559Block: big.NewInt(0), + CancunBlock: big.NewInt(0), + PragueBlock: big.NewInt(0), Ethash: new(params.EthashConfig), } - signer = types.LatestSigner(config) - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + signer = types.LatestSigner(config) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) - signedTx, err := types.SignTx(tx, signer, testKey) + signedTx, err := types.SignTx(tx, signer, key1) if err != nil { t.Fatalf("fail to sign tx: %v, err: %v", tx, err) } @@ -72,9 +76,36 @@ func TestStateProcessorErrors(t *testing.T) { Gas: gasLimit, To: &to, Value: big.NewInt(0), - }), signer, testKey) + }), signer, key1) return tx } + var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gasLimit, + Value: big.NewInt(0), + Data: data, + }), signer, key1) + return tx + } + var mkSetCodeTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, authlist []types.Authorization) *types.Transaction { + tx, err := types.SignTx(types.NewTx(&types.SetCodeTx{ + Nonce: nonce, + GasTipCap: uint256.MustFromBig(gasTipCap), + GasFeeCap: uint256.MustFromBig(gasFeeCap), + Gas: gasLimit, + To: to, + Value: new(uint256.Int), + AuthList: authlist, + }), signer, key1) + if err != nil { + t.Fatal(err) + } + return tx + } + { // Tests against a 'recent' chain definition var ( db = rawdb.NewMemoryDatabase() @@ -87,8 +118,9 @@ func TestStateProcessorErrors(t *testing.T) { }, }, } - genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) + genesis = gspec.MustCommit(db) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) + tooBigInitCode = [params.MaxInitCodeSize + 1]byte{} ) defer blockchain.Stop() bigNumber := new(big.Int).SetBytes(common.MaxHash.Bytes()) @@ -100,34 +132,34 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrNonceTooLow txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(12500000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(12500000000), nil), }, - want: "nonce too low: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", + want: "could not apply tx 1 [0xecd6a889a307155b3562cd64c86957e36fa58267cb4efbbe39aa692fd7aab09a]: nonce too low: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", }, { // ErrNonceTooHigh txs: []*types.Transaction{ makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), }, - want: "nonce too high: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", + want: "could not apply tx 0 [0xdebad714ca7f363bd0d8121c4518ad48fa469ca81b0a081be3d10c17460f751b]: nonce too high: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(12500000000), nil), }, - want: "gas limit reached", + want: "could not apply tx 0 [0x062b0e84f2d48f09f91e434fca8cb1fb864c4fb82f8bf27d58879ebe60c9f773]: gas limit reached", }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(12500000000), nil), }, - want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7", + want: "could not apply tx 0 [0x50f89093bf5ad7f4ae6f9e3bad44d4dc130247ea0429df0cf78873584a76dfa1]: insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000262500000000000", }, { // ErrInsufficientFunds txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), }, - want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", + want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", }, // ErrGasUintOverflow // One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow", @@ -135,21 +167,21 @@ func TestStateProcessorErrors(t *testing.T) { // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment { // ErrIntrinsicGas txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(12500000000), nil), }, - want: "intrinsic gas too low: have 20000, want 21000", + want: "could not apply tx 0 [0xa3484a466ffa8a88dc95e6ff520c853659dfc5507039c0b1452c2b845438771b]: intrinsic gas too low: have 20000, want 21000", }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(12500000000), nil), }, - want: "gas limit reached", + want: "could not apply tx 0 [0x062b0e84f2d48f09f91e434fca8cb1fb864c4fb82f8bf27d58879ebe60c9f773]: gas limit reached", }, { // ErrFeeCapTooLow txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), }, - want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000", + want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 12500000000", }, { // ErrTipVeryHigh txs: []*types.Transaction{ @@ -186,7 +218,26 @@ func TestStateProcessorErrors(t *testing.T) { }, want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", }, - }[8:] { + { // ErrMaxInitCodeSizeExceeded + txs: []*types.Transaction{ + mkDynamicCreationTx(0, 500000, common.Big0, big.NewInt(params.InitialBaseFee), tooBigInitCode[:]), + }, + want: "could not apply tx 0 [0x42c498ac252e8943de9e8ab3267113b0a0cde54543df1ab0711b0d87ccca3e03]: max initcode size exceeded: code size 49153 limit 49152", + }, + { // ErrIntrinsicGas: Not enough gas to cover init code + txs: []*types.Transaction{ + mkDynamicCreationTx(0, 54299, common.Big0, big.NewInt(params.InitialBaseFee), make([]byte, 320)), + }, + want: "could not apply tx 0 [0x83f0bd65f2c2ad82de0da306aa93dea5e47d4ba0cd9f23ec4ce3fd0a3246da1c]: intrinsic gas too low: have 54299, want 54300", + }, + { // ErrEmptyAuthList + txs: []*types.Transaction{ + mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil), + }, + want: "could not apply tx 0 [0x2fadb4fa7ccf8564edc21590f8d94a5b93a981b2bb2de8256978cb7361bc69de]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", + }, + // ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`. + } { block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { diff --git a/core/state_transition.go b/core/state_transition.go index 7a043d8d8174..6759954512dc 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -59,7 +59,7 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Authorization, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -101,6 +101,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, gas += uint64(len(accessList)) * params.TxAccessListAddressGas gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas } + if authList != nil { + gas += uint64(len(authList)) * params.CallNewAccountGas + } return gas, nil } @@ -127,6 +130,7 @@ type Message struct { BalanceTokenFee *big.Int Data []byte AccessList types.AccessList + AuthList []types.Authorization // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. @@ -149,6 +153,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, balanceFee, blo Value: tx.Value(), Data: tx.Data(), AccessList: tx.AccessList(), + AuthList: tx.AuthList(), SkipNonceChecks: false, SkipFromEOACheck: false, BalanceTokenFee: balanceFee, @@ -302,10 +307,10 @@ func (st *StateTransition) preCheck() error { } if !msg.SkipFromEOACheck { // Make sure the sender is an EOA - codeHash := st.state.GetCodeHash(msg.From) - if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash { - return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, - msg.From.Hex(), codeHash) + code := st.state.GetCode(msg.From) + _, delegated := types.ParseDelegation(code) + if len(code) > 0 && !delegated { + return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code)) } } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) @@ -333,6 +338,15 @@ func (st *StateTransition) preCheck() error { } } } + // Check that EIP-7702 authorization list signatures are well formed. + if msg.AuthList != nil { + if msg.To == nil { + return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From) + } + if len(msg.AuthList) == 0 { + return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) + } + } return st.buyGas() } @@ -373,7 +387,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsEIP1559) + gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.AuthList, contractCreation, rules.IsHomestead, rules.IsEIP1559) if err != nil { return nil, err } @@ -412,7 +426,26 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, msg.Value) } else { // Increment the nonce for the next transaction - st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) + st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1) + + // Apply EIP-7702 authorizations. + if msg.AuthList != nil { + for _, auth := range msg.AuthList { + // Note errors are ignored, we simply skip invalid authorizations here. + st.applyAuthorization(msg, &auth) + } + } + + // Perform convenience warming of sender's delegation target. Although the + // sender is already warmed in Prepare(..), it's possible a delegation to + // the account was deployed during this transaction. To handle correctly, + // simply wait until the final state of delegations is determined before + // performing the resolution and warming. + if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok { + st.state.AddAddressToAccessList(addr) + } + + // Execute the transaction's call. ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value) } @@ -449,6 +482,64 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, }, nil } +// validateAuthorization validates an EIP-7702 authorization against the state. +func (st *StateTransition) validateAuthorization(auth *types.Authorization) (authority common.Address, err error) { + // Verify chain ID is 0 or equal to current chain ID. + if auth.ChainID != 0 && st.evm.ChainConfig().ChainID.Uint64() != auth.ChainID { + return authority, ErrAuthorizationWrongChainID + } + // Limit nonce to 2^64-1 per EIP-2681. + if auth.Nonce+1 < auth.Nonce { + return authority, ErrAuthorizationNonceOverflow + } + // Validate signature values and recover authority. + authority, err = auth.Authority() + if err != nil { + return authority, fmt.Errorf("%w: %v", ErrAuthorizationInvalidSignature, err) + } + // Check the authority account + // 1) doesn't have code or has exisiting delegation + // 2) matches the auth's nonce + // + // Note it is added to the access list even if the authorization is invalid. + st.state.AddAddressToAccessList(authority) + code := st.state.GetCode(authority) + if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok { + return authority, ErrAuthorizationDestinationHasCode + } + if have := st.state.GetNonce(authority); have != auth.Nonce { + return authority, ErrAuthorizationNonceMismatch + } + return authority, nil +} + +// applyAuthorization applies an EIP-7702 code delegation to the state. +func (st *StateTransition) applyAuthorization(msg *Message, auth *types.Authorization) error { + authority, err := st.validateAuthorization(auth) + if err != nil { + return err + } + + // If the account already exists in state, refund the new account cost + // charged in the intrinsic calculation. + if st.state.Exist(authority) { + st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas) + } + + // Update nonce and account code. + st.state.SetNonce(authority, auth.Nonce+1) + if auth.Address == (common.Address{}) { + // Delegation to zero address means clear. + st.state.SetCode(authority, nil) + return nil + } + + // Otherwise install delegation to auth.Address. + st.state.SetCode(authority, types.AddressToDelegation(auth.Address)) + + return nil +} + func (st *StateTransition) refundGas(refundQuotient uint64) { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 4880e881553b..5cd74ef844c5 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -716,7 +716,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if !tx.IsSpecialTransaction() { // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.eip1559) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.AuthList(), tx.To() == nil, true, pool.eip1559) if err != nil { return err } diff --git a/core/types/gen_authorization.go b/core/types/gen_authorization.go new file mode 100644 index 000000000000..0d5ae865dcb5 --- /dev/null +++ b/core/types/gen_authorization.go @@ -0,0 +1,75 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/holiman/uint256" +) + +var _ = (*authorizationMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (a Authorization) MarshalJSON() ([]byte, error) { + type Authorization struct { + ChainID hexutil.Uint64 `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce hexutil.Uint64 `json:"nonce" gencodec:"required"` + V hexutil.Uint64 `json:"v" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` + } + var enc Authorization + enc.ChainID = hexutil.Uint64(a.ChainID) + enc.Address = a.Address + enc.Nonce = hexutil.Uint64(a.Nonce) + enc.V = hexutil.Uint64(a.V) + enc.R = a.R + enc.S = a.S + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *Authorization) UnmarshalJSON(input []byte) error { + type Authorization struct { + ChainID *hexutil.Uint64 `json:"chainId" gencodec:"required"` + Address *common.Address `json:"address" gencodec:"required"` + Nonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` + V *hexutil.Uint64 `json:"v" gencodec:"required"` + R *uint256.Int `json:"r" gencodec:"required"` + S *uint256.Int `json:"s" gencodec:"required"` + } + var dec Authorization + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' for Authorization") + } + a.ChainID = uint64(*dec.ChainID) + if dec.Address == nil { + return errors.New("missing required field 'address' for Authorization") + } + a.Address = *dec.Address + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for Authorization") + } + a.Nonce = uint64(*dec.Nonce) + if dec.V == nil { + return errors.New("missing required field 'v' for Authorization") + } + a.V = uint8(*dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for Authorization") + } + a.R = *dec.R + if dec.S == nil { + return errors.New("missing required field 's' for Authorization") + } + a.S = *dec.S + return nil +} diff --git a/core/types/receipt.go b/core/types/receipt.go index 4eb929fa5cba..f6966c06a1c1 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -215,7 +215,7 @@ func (r *Receipt) decodeTyped(b []byte) error { return errShortTypedReceipt } switch b[0] { - case DynamicFeeTxType, AccessListTxType: + case DynamicFeeTxType, AccessListTxType, SetCodeTxType: var data receiptRLP err := rlp.DecodeBytes(b[1:], &data) if err != nil { @@ -389,7 +389,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { } w.WriteByte(r.Type) switch r.Type { - case AccessListTxType, DynamicFeeTxType: + case AccessListTxType, DynamicFeeTxType, SetCodeTxType: rlp.Encode(w, data) default: // For unsupported types, write nothing. Since this is for diff --git a/core/types/transaction.go b/core/types/transaction.go index ee72b00074c5..d816f618f270 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -52,9 +52,10 @@ var ( // Transaction types. const ( - LegacyTxType = iota - AccessListTxType - DynamicFeeTxType + LegacyTxType = 0x00 + AccessListTxType = 0x01 + DynamicFeeTxType = 0x02 + SetCodeTxType = 0x04 ) // Transaction is an Ethereum transaction. @@ -202,6 +203,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { inner = new(AccessListTx) case DynamicFeeTxType: inner = new(DynamicFeeTx) + case SetCodeTxType: + inner = new(SetCodeTx) default: return nil, ErrTxTypeNotSupported } @@ -396,6 +399,15 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i return tx.EffectiveGasTipValue(baseFee).Cmp(other) } +// AuthList returns the authorizations list of the transaction. +func (tx *Transaction) AuthList() []Authorization { + setcodetx, ok := tx.inner.(*SetCodeTx) + if !ok { + return nil + } + return setcodetx.AuthList +} + // Hash returns the transaction hash. func (tx *Transaction) Hash() common.Hash { if hash := tx.hash.Load(); hash != nil { diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index b965ce36e12c..26fff1112da4 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -23,6 +23,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/holiman/uint256" ) // txJSON is the JSON representation of transactions. @@ -39,6 +40,7 @@ type txJSON struct { Value *hexutil.Big `json:"value"` Input *hexutil.Bytes `json:"input"` AccessList *AccessList `json:"accessList,omitempty"` + AuthorizationList []Authorization `json:"authorizationList,omitempty"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` @@ -121,8 +123,24 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.S = (*hexutil.Big)(itx.S) yparity := itx.V.Uint64() enc.YParity = (*hexutil.Uint64)(&yparity) - } + case *SetCodeTx: + enc.ChainID = (*hexutil.Big)(new(big.Int).SetUint64(itx.ChainID)) + enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) + enc.To = tx.To() + enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap.ToBig()) + enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap.ToBig()) + enc.Value = (*hexutil.Big)(itx.Value.ToBig()) + enc.Input = (*hexutil.Bytes)(&itx.Data) + enc.AccessList = &itx.AccessList + enc.AuthorizationList = itx.AuthList + enc.V = (*hexutil.Big)(itx.V.ToBig()) + enc.R = (*hexutil.Big)(itx.R.ToBig()) + enc.S = (*hexutil.Big)(itx.S.ToBig()) + yparity := itx.V.Uint64() + enc.YParity = (*hexutil.Uint64)(&yparity) + } return json.Marshal(&enc) } @@ -299,6 +317,81 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { } } + case SetCodeTxType: + var itx SetCodeTx + inner = &itx + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' in transaction") + } + itx.ChainID = dec.ChainID.ToInt().Uint64() + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' in transaction") + } + itx.Nonce = uint64(*dec.Nonce) + if dec.To == nil { + return errors.New("missing required field 'to' in transaction") + } + itx.To = *dec.To + if dec.Gas == nil { + return errors.New("missing required field 'gas' for txdata") + } + itx.Gas = uint64(*dec.Gas) + if dec.MaxPriorityFeePerGas == nil { + return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") + } + itx.GasTipCap = uint256.MustFromBig((*big.Int)(dec.MaxPriorityFeePerGas)) + if dec.MaxFeePerGas == nil { + return errors.New("missing required field 'maxFeePerGas' for txdata") + } + itx.GasFeeCap = uint256.MustFromBig((*big.Int)(dec.MaxFeePerGas)) + if dec.Value == nil { + return errors.New("missing required field 'value' in transaction") + } + itx.Value = uint256.MustFromBig((*big.Int)(dec.Value)) + if dec.Input == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Input + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.AuthorizationList == nil { + return errors.New("missing required field 'authorizationList' in transaction") + } + itx.AuthList = dec.AuthorizationList + + // signature R + var overflow bool + if dec.R == nil { + return errors.New("missing required field 'r' in transaction") + } + itx.R, overflow = uint256.FromBig((*big.Int)(dec.R)) + if overflow { + return errors.New("'r' value overflows uint256") + } + // signature S + if dec.S == nil { + return errors.New("missing required field 's' in transaction") + } + itx.S, overflow = uint256.FromBig((*big.Int)(dec.S)) + if overflow { + return errors.New("'s' value overflows uint256") + } + // signature V + vbig, err := dec.yParityValue() + if err != nil { + return err + } + itx.V, overflow = uint256.FromBig(vbig) + if overflow { + return errors.New("'v' value overflows uint256") + } + if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { + if err := sanityCheckSignature(vbig, itx.R.ToBig(), itx.S.ToBig(), false); err != nil { + return err + } + } + default: return ErrTxTypeNotSupported } @@ -306,5 +399,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { // Now set the inner transaction. tx.setDecoded(inner, 0) + // TODO: check hash here? return nil } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 31fe31cc56e2..508b67f5b12a 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -20,6 +20,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "math" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -41,6 +42,8 @@ type sigCache struct { func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { var signer Signer switch { + case config.IsPrague(blockNumber): + signer = NewPragueSigner(config.ChainID) case config.IsEIP1559(blockNumber): signer = NewLondonSigner(config.ChainID) case config.IsEIP155(blockNumber): @@ -61,15 +64,20 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { // 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 { + var signer Signer if config.ChainID != nil { - if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil { - return NewLondonSigner(config.ChainID) - } - if config.EIP155Block != nil { - return NewEIP155Signer(config.ChainID) + switch { + case common.PragueBlock.Int64() != math.MaxInt64 || config.PragueBlock != nil: + signer = NewPragueSigner(config.ChainID) + case common.Eip1559Block.Int64() != math.MaxInt64 || config.Eip1559Block != nil: + signer = NewLondonSigner(config.ChainID) + case config.EIP155Block != nil: + signer = NewEIP155Signer(config.ChainID) + default: + signer = HomesteadSigner{} } } - return HomesteadSigner{} + return signer } // LatestSignerForChainID returns the 'most permissive' Signer available. Specifically, @@ -80,10 +88,13 @@ func LatestSigner(config *params.ChainConfig) Signer { // 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{} + var signer Signer + if chainID != nil { + signer = NewPragueSigner(chainID) + } else { + signer = HomesteadSigner{} } - return NewLondonSigner(chainID) + return signer } // SignTx signs the transaction using the given signer and private key. @@ -164,6 +175,76 @@ type Signer interface { Equal(Signer) bool } +type pragueSigner struct{ londonSigner } + +// NewPragueSigner returns a signer that accepts +// - EIP-7702 set code transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewPragueSigner(chainId *big.Int) Signer { + signer, _ := NewLondonSigner(chainId).(londonSigner) + return pragueSigner{signer} +} + +func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != SetCodeTxType { + return s.londonSigner.Sender(tx) + } + V, R, S := tx.RawSignatureValues() + + // Set code 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)) + if tx.ChainId().Cmp(s.chainId) != 0 { + return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) + } + return recoverPlain(s.Hash(tx), R, S, V, true) +} + +func (s pragueSigner) Equal(s2 Signer) bool { + x, ok := s2.(pragueSigner) + return ok && x.chainId.Cmp(s.chainId) == 0 +} + +func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + txdata, ok := tx.inner.(*SetCodeTx) + if !ok { + return s.londonSigner.SignatureValues(tx, sig) + } + // 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 != 0 && new(big.Int).SetUint64(txdata.ChainID).Cmp(s.chainId) != 0 { + return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) + } + R, S, _ = decodeSignature(sig) + V = big.NewInt(int64(sig[64])) + return R, S, V, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s pragueSigner) Hash(tx *Transaction) common.Hash { + if tx.Type() != SetCodeTxType { + return s.londonSigner.Hash(tx) + } + return prefixedRlpHash( + tx.Type(), + []interface{}{ + s.chainId, + tx.Nonce(), + tx.GasTipCap(), + tx.GasFeeCap(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + tx.AccessList(), + tx.AuthList(), + }) +} + type londonSigner struct{ eip2930Signer } // NewLondonSigner returns a signer that accepts diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go new file mode 100644 index 000000000000..1137c03d9fe1 --- /dev/null +++ b/core/types/tx_setcode.go @@ -0,0 +1,226 @@ +// Copyright 2024 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 . + +package types + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/rlp" + "github.com/holiman/uint256" +) + +// DelegationPrefix is used by code to denote the account is delegating to +// another account. +var DelegationPrefix = []byte{0xef, 0x01, 0x00} + +// ParseDelegation tries to parse the address from a delegation slice. +func ParseDelegation(b []byte) (common.Address, bool) { + if len(b) != 23 || !bytes.HasPrefix(b, DelegationPrefix) { + return common.Address{}, false + } + return common.BytesToAddress(b[len(DelegationPrefix):]), true +} + +// AddressToDelegation adds the delegation prefix to the specified address. +func AddressToDelegation(addr common.Address) []byte { + return append(DelegationPrefix, addr.Bytes()...) +} + +// SetCodeTx implements the EIP-7702 transaction type which temporarily installs +// the code at the signer's address. +type SetCodeTx struct { + ChainID uint64 + Nonce uint64 + GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas + GasFeeCap *uint256.Int // a.k.a. maxFeePerGas + Gas uint64 + To common.Address + Value *uint256.Int + Data []byte + AccessList AccessList + AuthList []Authorization + + // Signature values + V *uint256.Int `json:"v" gencodec:"required"` + R *uint256.Int `json:"r" gencodec:"required"` + S *uint256.Int `json:"s" gencodec:"required"` +} + +//go:generate go run github.com/fjl/gencodec -type Authorization -field-override authorizationMarshaling -out gen_authorization.go + +// Authorization is an authorization from an account to deploy code at its address. +type Authorization struct { + ChainID uint64 `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"v" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` +} + +// field type overrides for gencodec +type authorizationMarshaling struct { + ChainID hexutil.Uint64 + Nonce hexutil.Uint64 + V hexutil.Uint64 +} + +// SignAuth signs the provided authorization. +func SignAuth(auth Authorization, prv *ecdsa.PrivateKey) (Authorization, error) { + sighash := auth.sigHash() + sig, err := crypto.Sign(sighash[:], prv) + if err != nil { + return Authorization{}, err + } + return auth.withSignature(sig), nil +} + +// withSignature updates the signature of an Authorization to be equal the +// decoded signature provided in sig. +func (a *Authorization) withSignature(sig []byte) Authorization { + r, s, _ := decodeSignature(sig) + return Authorization{ + ChainID: a.ChainID, + Address: a.Address, + Nonce: a.Nonce, + V: sig[64], + R: *uint256.MustFromBig(r), + S: *uint256.MustFromBig(s), + } +} + +func (a *Authorization) sigHash() common.Hash { + return prefixedRlpHash(0x05, []any{ + a.ChainID, + a.Address, + a.Nonce, + }) +} + +// Authority recovers the the authorizing account of an authorization. +func (a *Authorization) Authority() (common.Address, error) { + sighash := a.sigHash() + if !crypto.ValidateSignatureValues(a.V, a.R.ToBig(), a.S.ToBig(), true) { + return common.Address{}, ErrInvalidSig + } + // encode the signature in uncompressed format + var sig [crypto.SignatureLength]byte + a.R.WriteToSlice(sig[:32]) + a.S.WriteToSlice(sig[32:64]) + sig[64] = a.V + // recover the public key from the signature + pub, err := crypto.Ecrecover(sighash[:], sig[:]) + if err != nil { + return common.Address{}, err + } + if len(pub) == 0 || pub[0] != 4 { + return common.Address{}, errors.New("invalid public key") + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pub[1:])[12:]) + return addr, nil +} + +// copy creates a deep copy of the transaction data and initializes all fields. +func (tx *SetCodeTx) copy() TxData { + cpy := &SetCodeTx{ + Nonce: tx.Nonce, + To: tx.To, + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + // These are copied below. + AccessList: make(AccessList, len(tx.AccessList)), + AuthList: make([]Authorization, len(tx.AuthList)), + Value: new(uint256.Int), + ChainID: tx.ChainID, + GasTipCap: new(uint256.Int), + GasFeeCap: new(uint256.Int), + V: new(uint256.Int), + R: new(uint256.Int), + S: new(uint256.Int), + } + copy(cpy.AccessList, tx.AccessList) + copy(cpy.AuthList, tx.AuthList) + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.GasTipCap != nil { + cpy.GasTipCap.Set(tx.GasTipCap) + } + if tx.GasFeeCap != nil { + cpy.GasFeeCap.Set(tx.GasFeeCap) + } + 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 *SetCodeTx) txType() byte { return SetCodeTxType } +func (tx *SetCodeTx) chainID() *big.Int { return big.NewInt(int64(tx.ChainID)) } +func (tx *SetCodeTx) accessList() AccessList { return tx.AccessList } +func (tx *SetCodeTx) data() []byte { return tx.Data } +func (tx *SetCodeTx) gas() uint64 { return tx.Gas } +func (tx *SetCodeTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() } +func (tx *SetCodeTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() } +func (tx *SetCodeTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() } +func (tx *SetCodeTx) value() *big.Int { return tx.Value.ToBig() } +func (tx *SetCodeTx) nonce() uint64 { return tx.Nonce } +func (tx *SetCodeTx) to() *common.Address { tmp := tx.To; return &tmp } + +func (tx *SetCodeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + if baseFee == nil { + return dst.Set(tx.GasFeeCap.ToBig()) + } + tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee) + if tip.Cmp(tx.GasTipCap.ToBig()) > 0 { + tip.Set(tx.GasTipCap.ToBig()) + } + return tip.Add(tip, baseFee) +} + +func (tx *SetCodeTx) rawSignatureValues() (v, r, s *big.Int) { + return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig() +} + +func (tx *SetCodeTx) setSignatureValues(chainID, v, r, s *big.Int) { + tx.ChainID = chainID.Uint64() + tx.V.SetFromBig(v) + tx.R.SetFromBig(r) + tx.S.SetFromBig(s) +} + +func (tx *SetCodeTx) encode(b *bytes.Buffer) error { + return rlp.Encode(b, tx) +} + +func (tx *SetCodeTx) decode(input []byte) error { + return rlp.DecodeBytes(input, tx) +} diff --git a/core/types/tx_setcode_test.go b/core/types/tx_setcode_test.go new file mode 100644 index 000000000000..9223b0e4c664 --- /dev/null +++ b/core/types/tx_setcode_test.go @@ -0,0 +1,70 @@ +// Copyright 2024 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 . + +package types + +import ( + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" +) + +// TestParseDelegation tests a few possible delegation designator values and +// ensures they are parsed correctly. +func TestParseDelegation(t *testing.T) { + addr := common.Address{0x42} + for _, tt := range []struct { + val []byte + want *common.Address + }{ + { // simple correct delegation + val: append(DelegationPrefix, addr.Bytes()...), + want: &addr, + }, + { // wrong address size + val: append(DelegationPrefix, addr.Bytes()[0:19]...), + }, + { // short address + val: append(DelegationPrefix, 0x42), + }, + { // long address + val: append(append(DelegationPrefix, addr.Bytes()...), 0x42), + }, + { // wrong prefix size + val: append(DelegationPrefix[:2], addr.Bytes()...), + }, + { // wrong prefix + val: append([]byte{0xef, 0x01, 0x01}, addr.Bytes()...), + }, + { // wrong prefix + val: append([]byte{0xef, 0x00, 0x00}, addr.Bytes()...), + }, + { // no prefix + val: addr.Bytes(), + }, + { // no address + val: DelegationPrefix, + }, + } { + got, ok := ParseDelegation(tt.val) + if ok && tt.want == nil { + t.Fatalf("expected fail, got %s", got.Hex()) + } + if !ok && tt.want != nil { + t.Fatalf("failed to parse, want %s", tt.want.Hex()) + } + } +} diff --git a/core/vm/eips.go b/core/vm/eips.go index 5715ba4a59ef..cfdf065bbfa4 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -18,8 +18,11 @@ package vm import ( "fmt" + "math" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/holiman/uint256" ) @@ -36,6 +39,7 @@ var activators = map[int]func(*JumpTable){ 1884: enable1884, 1344: enable1344, 1153: enable1153, + 7702: enable7702, } // EnableEIP enables the given EIP on the config. @@ -308,3 +312,68 @@ func enable6780(jt *JumpTable) { maxStack: maxStack(1, 0), } } + +// opExtCodeCopyEIP7702 implements the EIP-7702 variation of opExtCodeCopy. +func opExtCodeCopyEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + stack = scope.Stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = math.MaxUint64 + } + code := interpreter.evm.StateDB.GetCode(common.Address(a.Bytes20())) + if _, ok := types.ParseDelegation(code); ok { + code = types.DelegationPrefix[:2] + } + codeCopy := getData(code, uint64CodeOffset, length.Uint64()) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + + return nil, nil +} + +// opExtCodeSizeEIP7702 implements the EIP-7702 variation of opExtCodeSize. +func opExtCodeSizeEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() + code := interpreter.evm.StateDB.GetCode(common.Address(slot.Bytes20())) + if _, ok := types.ParseDelegation(code); ok { + code = types.DelegationPrefix[:2] + } + slot.SetUint64(uint64(len(code))) + return nil, nil +} + +// opExtCodeHashEIP7702 implements the EIP-7702 variation of opExtCodeHash. +func opExtCodeHashEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() + addr := common.Address(slot.Bytes20()) + if interpreter.evm.StateDB.Empty(addr) { + slot.Clear() + return nil, nil + } + code := interpreter.evm.StateDB.GetCode(addr) + if _, ok := types.ParseDelegation(code); ok { + // If the code is a delegation, return the prefix without version. + slot.SetBytes(crypto.Keccak256(types.DelegationPrefix[:2])) + } else { + // Otherwise, return normal code hash. + slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(addr).Bytes()) + } + return nil, nil +} + +// enable7702 the EIP-7702 changes to support delegation designators. +func enable7702(jt *JumpTable) { + jt[EXTCODECOPY].execute = opExtCodeCopyEIP7702 + jt[EXTCODESIZE].execute = opExtCodeSizeEIP7702 + jt[EXTCODEHASH].execute = opExtCodeHashEIP7702 + + jt[CALL].dynamicGas = gasCallEIP7702 + jt[CALLCODE].dynamicGas = gasCallCodeEIP7702 + jt[STATICCALL].dynamicGas = gasStaticCallEIP7702 + jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702 +} diff --git a/core/vm/evm.go b/core/vm/evm.go index d3d278fda14d..36fa28ae34c4 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -199,7 +199,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - code := evm.StateDB.GetCode(addr) + code := evm.resolveCode(addr) if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { @@ -207,7 +207,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // If the account has no code, we can abort here // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -267,7 +267,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -314,7 +314,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by addrCopy := addr // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -371,7 +371,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. @@ -512,6 +512,35 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } +// resolveCode returns the code associated with the provided account. After +// Prague, it can also resolve code pointed to by a delegation designator. +func (evm *EVM) resolveCode(addr common.Address) []byte { + code := evm.StateDB.GetCode(addr) + if !evm.chainRules.IsPrague { + return code + } + if target, ok := types.ParseDelegation(code); ok { + // Note we only follow one level of delegation. + return evm.StateDB.GetCode(target) + } + return code +} + +// resolveCodeHash returns the code hash associated with the provided address. +// After Prague, it can also resolve code hash of the account pointed to by a +// delegation designator. Although this is not accessible in the EVM it is used +// internally to associate jumpdest analysis to code. +func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash { + if evm.chainRules.IsPrague { + code := evm.StateDB.GetCode(addr) + if target, ok := types.ParseDelegation(code); ok { + // Note we only follow one level of delegation. + return evm.StateDB.GetCodeHash(target) + } + } + return evm.StateDB.GetCodeHash(addr) +} + // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } diff --git a/core/vm/interface.go b/core/vm/interface.go index d06184477b88..8231ce2e6a4d 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -38,7 +38,9 @@ type StateDB interface { GetCodeHash(common.Address) common.Hash GetCode(common.Address) []byte - SetCode(common.Address, []byte) + + // SetCode sets the new code for the address, and returns the previous code, if any. + SetCode(common.Address, []byte) []byte GetCodeSize(common.Address) int AddRefund(uint64) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index cb1c6baa5178..c0ad9acdb013 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -100,6 +100,8 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. var table *JumpTable switch { + case evm.chainRules.IsPrague: + table = &pragueInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsEIP1559: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index a32a787a4e39..adbfe5a07304 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -58,6 +58,7 @@ var ( shanghaiInstructionSet = newShanghaiInstructionSet() eip1559InstructionSet = newEip1559InstructionSet() cancunInstructionSet = newCancunInstructionSet() + pragueInstructionSet = newPragueInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -81,6 +82,12 @@ func validate(jt JumpTable) JumpTable { return jt } +func newPragueInstructionSet() JumpTable { + instructionSet := newCancunInstructionSet() + enable7702(&instructionSet) // EIP-7702 Setcode transaction type + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newEip1559InstructionSet() enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index d3225bbd2298..777c9ee9da52 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -22,6 +22,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/tracing" + "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" ) @@ -248,3 +249,70 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { } return gasFunc } + +var ( + gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall) + gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall) + gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall) + gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode) +) + +func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + total uint64 // total dynamic gas used + addr = common.Address(stack.Back(1).Bytes20()) + ) + + // Check slot presence in the access list + if !evm.StateDB.AddressInAccessList(addr) { + evm.StateDB.AddAddressToAccessList(addr) + // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so + // the cost to charge for cold access, if any, is Cold - Warm + coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 + // Charge the remaining difference here already, to correctly calculate available + // gas for call + if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { + return 0, ErrOutOfGas + } + total += coldCost + } + + // Check if code is a delegation and if so, charge for resolution. + if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok { + var cost uint64 + if evm.StateDB.AddressInAccessList(target) { + cost = params.WarmStorageReadCostEIP2929 + } else { + evm.StateDB.AddAddressToAccessList(target) + cost = params.ColdAccountAccessCostEIP2929 + } + if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { + return 0, ErrOutOfGas + } + total += cost + } + + // Now call the old calculator, which takes into account + // - create new account + // - transfer value + // - memory expansion + // - 63/64ths rule + old, err := oldCalculator(evm, contract, stack, mem, memorySize) + if err != nil { + return old, err + } + + // Temporarily add the gas charge back to the contract and return value. By + // adding it to the return, it will be charged outside of this function, as + // part of the dynamic gas. This will ensure it is correctly reported to + // tracers. + contract.Gas += total + + var overflow bool + if total, overflow = math.SafeAdd(old, total); overflow { + return 0, ErrGasUintOverflow + } + return total, nil + } +} diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 9d70376074ea..73c12f9143fe 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/asm" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm/program" @@ -802,3 +803,83 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) { benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b) benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b) } + +// TestDelegatedAccountAccessCost tests that calling an account with an EIP-7702 +// delegation designator incurs the correct amount of gas based on the tracer. +func TestDelegatedAccountAccessCost(t *testing.T) { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa"))) + statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes()) + + for i, tc := range []struct { + code []byte + step int + want uint64 + }{ + { // CALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP), + }, + step: 7, + want: 5455, + }, + { // CALLCODE(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP), + }, + step: 7, + want: 5455, + }, + { // DELEGATECALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP), + }, + step: 6, + want: 5455, + }, + { // STATICCALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP), + }, + step: 6, + want: 5455, + }, + { // SELFDESTRUCT(0xff): should not be affected by resolution + code: []byte{ + byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT), + }, + step: 1, + want: 7600, + }, + } { + var step = 0 + var have = uint64(0) + Execute(tc.code, nil, &Config{ + ChainConfig: params.MergedTestChainConfig, + State: statedb, + EVMConfig: vm.Config{ + Tracer: &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + // Uncomment to investigate failures: + t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost) + if step == tc.step { + have = cost + } + step++ + }, + }, + }, + }) + if want := tc.want; have != want { + t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json new file mode 100644 index 000000000000..261150145fc0 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json @@ -0,0 +1,80 @@ +{ + "genesis": { + "baseFeePerGas": "7", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "36306944", + "extraData": "0xd983010e00846765746888676f312e32312e308664617277696e", + "gasLimit": "15639172", + "hash": "0xc682259fda061bb9ce8ccb491d5b2d436cb73daf04e1025dd116d045ce4ad28c", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0xae1a5ba939a4c9ac38aabeff361169fb55a6fc2c9511457e0be6eff9514faec0", + "nonce": "0x0000000000000000", + "number": "315", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a", + "timestamp": "1709626771", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, + "0x000000000000000000000000000000000000aaaa": { + "code": "0x6000600060006000600173703c4b2bd70c169f5717101caee543299fc946c75af1", + "balance": "0x0" + }, + "0x000000000000000000000000000000000000bbbb": { + "code": "0x6042604255", + "balance": "0x0" + }, + "0x703c4b2bd70c169f5717101caee543299fc946c7": { + "balance": "0xde0b6b3a7640000" + }, + "0x71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xde0b6b3a7640000" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "eip1559Block": 0, + "cancunBlock": 0, + "pragueBlock": 0, + "terminalTotalDifficulty": 0 + } + }, + "context": { + "number": "316", + "difficulty": "0", + "timestamp": "1709626785", + "gasLimit": "15654443", + "miner": "0x0000000000000000000000000000000000000000", + "baseFeePerGas": "7" + }, + "input": "04f90126820539800285012a05f2008307a1209471562b71999873db5b286df957af199ec94617f78080c0f8baf85c82053994000000000000000000000000000000000000aaaa0101a07ed17af7d2d2b9ba7d797a202125bf505b9a0f962a67b3b61b56783d8faf7461a001b73b6e586edc706dce6c074eaec28692fa6359fb3446a2442f36777e1c0669f85a8094000000000000000000000000000000000000bbbb8001a05011890f198f0356a887b0779bde5afa1ed04e6acb1e3f37f8f18c7b6f521b98a056c3fa3456b103f3ef4a0acb4b647b9cab9ec4bc68fbcdf1e10b49fb2bcbcf6101a0167b0ecfc343a497095c22ee4270d3cc3b971cc3599fc73bbff727e0d2ed432da01c003c72306807492bf1150e39b2f79da23b49a4e83eb6e9209ae30d3572368f", + "result": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, + "0x703c4b2bd70c169f5717101caee543299fc946c7": { + "balance": "0xde0b6b3a7640000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000042": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xde0b6b3a7640000" + } + } +} diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 067bee71247f..8b701b7d593a 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -159,6 +159,15 @@ func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction t.lookupAccount(from) t.lookupAccount(t.to) t.lookupAccount(env.Coinbase) + + // Add accounts with authorizations to the prestate before they get applied. + for _, auth := range tx.AuthList() { + addr, err := auth.Authority() + if err != nil { + continue + } + t.lookupAccount(addr) + } } func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9756aa88a80c..3493a3296106 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1654,26 +1654,27 @@ func (api *BlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context.Cont // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` - GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - Type hexutil.Uint64 `json:"type"` - Accesses *types.AccessList `json:"accessList,omitempty"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` - YParity *hexutil.Uint64 `json:"yParity,omitempty"` + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + AuthorizationList []types.Authorization `json:"authorizationList,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` } // newRPCTransaction returns a transaction that will serialize to the RPC @@ -1725,6 +1726,22 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } + + case types.SetCodeTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // if the transaction has been mined, compute the effective gas price + if baseFee != nil && blockHash != (common.Hash{}) { + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) + } else { + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + } + result.AuthorizationList = tx.AuthList() } return result } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 621f353d3e01..6973dae657d6 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -31,6 +31,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rpc" + "github.com/holiman/uint256" ) // TransactionArgs represents the arguments to construct a new transaction @@ -54,6 +55,9 @@ type TransactionArgs struct { // Introduced by AccessListTxType transaction. AccessList *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` + + // For SetCodeTxType + AuthorizationList []types.Authorization `json:"authorizationList"` } // from retrieves the transaction sender address. @@ -343,6 +347,8 @@ func (args *TransactionArgs) ToMessage(b AccountBackend, baseFee *big.Int, skipN func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { usedType := types.LegacyTxType switch { + case args.AuthorizationList != nil || defaultType == types.SetCodeTxType: + usedType = types.SetCodeTxType case args.MaxFeePerGas != nil || defaultType == types.DynamicFeeTxType: usedType = types.DynamicFeeTxType case args.AccessList != nil || defaultType == types.AccessListTxType: @@ -354,6 +360,28 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { } var data types.TxData switch usedType { + case types.SetCodeTxType: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList + } + authList := []types.Authorization{} + if args.AuthorizationList != nil { + authList = args.AuthorizationList + } + data = &types.SetCodeTx{ + To: *args.To, + ChainID: args.ChainID.ToInt().Uint64(), + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)), + GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)), + Value: uint256.MustFromBig((*big.Int)(args.Value)), + Data: args.data(), + AccessList: al, + AuthList: authList, + } + case types.DynamicFeeTxType: al := types.AccessList{} if args.AccessList != nil { @@ -370,6 +398,7 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { Data: args.data(), AccessList: al, } + case types.AccessListTxType: data = &types.AccessListTx{ To: args.To, @@ -381,6 +410,7 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { Data: args.data(), AccessList: *args.AccessList, } + default: data = &types.LegacyTx{ To: args.To, diff --git a/params/config.go b/params/config.go index 556ac46478ae..283268347fd2 100644 --- a/params/config.go +++ b/params/config.go @@ -396,6 +396,8 @@ var ( }, } + // TestChainConfig contains every protocol change (EIPs) introduced + // and accepted by the Ethereum core developers for testing purposes. TestChainConfig = &ChainConfig{ ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), @@ -410,6 +412,30 @@ var ( Clique: nil, XDPoS: nil, } + + // MergedTestChainConfig contains every protocol change (EIPs) introduced + // and accepted by the Ethereum core developers for testing purposes. + MergedTestChainConfig = &ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + Eip1559Block: big.NewInt(0), + CancunBlock: big.NewInt(0), + PragueBlock: big.NewInt(0), + Ethash: new(EthashConfig), + Clique: nil, + } TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -442,6 +468,7 @@ type ChainConfig struct { ShanghaiBlock *big.Int `json:"shanghaiBlock,omitempty"` Eip1559Block *big.Int `json:"eip1559Block,omitempty"` CancunBlock *big.Int `json:"cancunBlock,omitempty"` + PragueBlock *big.Int `json:"pragueBlock,omitempty"` // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -791,6 +818,10 @@ func (c *ChainConfig) Description() string { if c.CancunBlock != nil { cancunBlock = c.CancunBlock } + pragueBlock := common.PragueBlock + if c.PragueBlock != nil { + pragueBlock = c.PragueBlock + } var banner = "Chain configuration:\n" banner += fmt.Sprintf(" - ChainID: %-8v\n", c.ChainID) @@ -809,6 +840,7 @@ func (c *ChainConfig) Description() string { banner += fmt.Sprintf(" - Shanghai: %-8v\n", shanghaiBlock) banner += fmt.Sprintf(" - Eip1559: %-8v\n", eip1559Block) banner += fmt.Sprintf(" - Cancun: %-8v\n", cancunBlock) + banner += fmt.Sprintf(" - Prague: %-8v\n", pragueBlock) banner += fmt.Sprintf(" - TIPUpgradeReward: %-8v\n", common.TIPUpgradeReward) banner += fmt.Sprintf(" - TIPEpochHalving: %-8v\n", common.TIPEpochHalving) banner += fmt.Sprintf(" - Engine: %v", engine) @@ -886,6 +918,10 @@ func (c *ChainConfig) IsCancun(num *big.Int) bool { return isForked(common.CancunBlock, num) || isForked(c.CancunBlock, num) } +func (c *ChainConfig) IsPrague(num *big.Int) bool { + return isForked(common.PragueBlock, num) || isForked(c.PragueBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -1031,6 +1067,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.CancunBlock, newcfg.CancunBlock, head) { return newCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock) } + if isForkIncompatible(c.PragueBlock, newcfg.PragueBlock, head) { + return newCompatError("Prague fork block", c.PragueBlock, newcfg.PragueBlock) + } if !XDPoSConfigEqual(c.XDPoS, newcfg.XDPoS) { storedblock := big.NewInt(1) if c.XDPoS != nil && c.XDPoS.V2 != nil && c.XDPoS.V2.SwitchBlock != nil { @@ -1106,14 +1145,23 @@ func (err *ConfigCompatError) Error() string { // Rules is a one time interface meaning that it shouldn't be used in between transition // phases. type Rules struct { - ChainId *big.Int - IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool - IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin, IsLondon bool - IsMerge, IsShanghai bool - IsXDCxDisable bool - IsEIP1559 bool - IsCancun bool + ChainId *big.Int + IsHomestead bool + IsEIP150 bool + IsEIP155 bool + IsEIP158 bool + IsByzantium bool + IsConstantinople bool + IsPetersburg bool + IsIstanbul bool + IsBerlin bool + IsLondon bool + IsMerge bool + IsShanghai bool + IsXDCxDisable bool + IsEIP1559 bool + IsCancun bool + IsPrague bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -1138,5 +1186,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsXDCxDisable: c.IsXDCxDisable(num), IsEIP1559: c.IsEIP1559(num), IsCancun: c.IsCancun(num), + IsPrague: c.IsPrague(num), } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 364b6efd335c..f4450233092d 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -65,8 +65,9 @@ const ( SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list - TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list + TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list + TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list + TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702 TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. diff --git a/tests/gen_stauthorization.go b/tests/gen_stauthorization.go new file mode 100644 index 000000000000..448565042119 --- /dev/null +++ b/tests/gen_stauthorization.go @@ -0,0 +1,74 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package tests + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/math" +) + +var _ = (*stAuthorizationMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s stAuthorization) MarshalJSON() ([]byte, error) { + type stAuthorization struct { + ChainID math.HexOrDecimal64 + Address common.Address `json:"address" gencodec:"required"` + Nonce math.HexOrDecimal64 `json:"nonce" gencodec:"required"` + V math.HexOrDecimal64 `json:"v" gencodec:"required"` + R *math.HexOrDecimal256 `json:"r" gencodec:"required"` + S *math.HexOrDecimal256 `json:"s" gencodec:"required"` + } + var enc stAuthorization + enc.ChainID = math.HexOrDecimal64(s.ChainID) + enc.Address = s.Address + enc.Nonce = math.HexOrDecimal64(s.Nonce) + enc.V = math.HexOrDecimal64(s.V) + enc.R = (*math.HexOrDecimal256)(s.R) + enc.S = (*math.HexOrDecimal256)(s.S) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *stAuthorization) UnmarshalJSON(input []byte) error { + type stAuthorization struct { + ChainID *math.HexOrDecimal64 + Address *common.Address `json:"address" gencodec:"required"` + Nonce *math.HexOrDecimal64 `json:"nonce" gencodec:"required"` + V *math.HexOrDecimal64 `json:"v" gencodec:"required"` + R *math.HexOrDecimal256 `json:"r" gencodec:"required"` + S *math.HexOrDecimal256 `json:"s" gencodec:"required"` + } + var dec stAuthorization + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ChainID != nil { + s.ChainID = uint64(*dec.ChainID) + } + if dec.Address == nil { + return errors.New("missing required field 'address' for stAuthorization") + } + s.Address = *dec.Address + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for stAuthorization") + } + s.Nonce = uint64(*dec.Nonce) + if dec.V == nil { + return errors.New("missing required field 'v' for stAuthorization") + } + s.V = uint8(*dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for stAuthorization") + } + s.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for stAuthorization") + } + s.S = (*big.Int)(dec.S) + return nil +} diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go index ac8989372780..24ca66e8c116 100644 --- a/tests/gen_sttransaction.go +++ b/tests/gen_sttransaction.go @@ -26,6 +26,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { GasLimit []math.HexOrDecimal64 `json:"gasLimit"` Value []string `json:"value"` PrivateKey hexutil.Bytes `json:"secretKey"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } var enc stTransaction enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) @@ -43,6 +44,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { } enc.Value = s.Value enc.PrivateKey = s.PrivateKey + enc.AuthorizationList = s.AuthorizationList return json.Marshal(&enc) } @@ -59,6 +61,7 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { GasLimit []math.HexOrDecimal64 `json:"gasLimit"` Value []string `json:"value"` PrivateKey *hexutil.Bytes `json:"secretKey"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } var dec stTransaction if err := json.Unmarshal(input, &dec); err != nil { @@ -97,5 +100,8 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { if dec.PrivateKey != nil { s.PrivateKey = *dec.PrivateKey } + if dec.AuthorizationList != nil { + s.AuthorizationList = dec.AuthorizationList + } return nil } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index f607a8487284..6c3f3bffea20 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -37,6 +37,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -107,6 +108,7 @@ type stTransaction struct { GasLimit []uint64 `json:"gasLimit"` Value []string `json:"value"` PrivateKey []byte `json:"secretKey"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } type stTransactionMarshaling struct { @@ -118,6 +120,27 @@ type stTransactionMarshaling struct { PrivateKey hexutil.Bytes } +//go:generate go run github.com/fjl/gencodec -type stAuthorization -field-override stAuthorizationMarshaling -out gen_stauthorization.go + +// Authorization is an authorization from an account to deploy code at it's address. +type stAuthorization struct { + ChainID uint64 + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` +} + +// field type overrides for gencodec +type stAuthorizationMarshaling struct { + ChainID math.HexOrDecimal64 + Nonce math.HexOrDecimal64 + V math.HexOrDecimal64 + R *math.HexOrDecimal256 + S *math.HexOrDecimal256 +} + // Subtests returns all valid subtests of the test. func (t *StateTest) Subtests() []StateSubtest { var sub []StateSubtest @@ -284,6 +307,20 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big if gasPrice == nil { return nil, errors.New("no gas price provided") } + var authList []types.Authorization + if tx.AuthorizationList != nil { + authList = make([]types.Authorization, len(tx.AuthorizationList)) + for i, auth := range tx.AuthorizationList { + authList[i] = types.Authorization{ + ChainID: auth.ChainID, + Address: auth.Address, + Nonce: auth.Nonce, + V: auth.V, + R: *uint256.MustFromBig(auth.R), + S: *uint256.MustFromBig(auth.S), + } + } + } msg := &core.Message{ From: from, @@ -296,6 +333,7 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big GasTipCap: tx.MaxPriorityFeePerGas, Data: data, AccessList: accessList, + AuthList: authList, SkipNonceChecks: false, SkipFromEOACheck: false, } From 170656ca03368b80de26d6d9b3a8dcb6f0caaafa Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 24 Jun 2025 13:52:18 +0800 Subject: [PATCH 10/10] eth/tracers: prestate lookup EIP7702 delegation account #32080 Implement https://github.com/ethereum/go-ethereum/issues/32078 Parse and lookup the delegation account if EIP7702 is enabled. --------- Signed-off-by: jsvisa --- .../prestate_tracer/7702_delegate.json | 163 ++++++++++++++++++ .../testdata/prestate_tracer/setcode_tx.json | 4 + eth/tracers/native/prestate.go | 44 +++-- 3 files changed, 197 insertions(+), 14 deletions(-) create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/7702_delegate.json diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/7702_delegate.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/7702_delegate.json new file mode 100644 index 000000000000..060d06f5d258 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/7702_delegate.json @@ -0,0 +1,163 @@ +{ + "genesis": { + "baseFeePerGas": "0x389ef14a", + "blobGasUsed": "0x120000", + "difficulty": "0x0", + "excessBlobGas": "0x20000", + "extraData": "0x6265617665726275696c642e6f7267", + "gasLimit": "0x225da53", + "hash": "0x9c1d4eb19d30fa830e02493f5108ddfd49f2736983cecb6b3748b79e78f98d14", + "miner": "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5", + "mixHash": "0x82bbbb55d5e4edf221aadaefe697f265210cc4afd8a0fa977769da5be8c100c0", + "nonce": "0x0000000000000000", + "number": "0x15b589d", + "parentBeaconBlockRoot": "0x64c714ee5b2d66ea6fd1f6633e41bf2863955c0b7a9e925a241f5e4e3c19f81e", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stateRoot": "0x5c700c05128ae491d83b5602fd96f0faa487562441bef96ec9474c144820ed10", + "timestamp": "0x6858a55f", + "alloc": { + "0x17816e9a858b161c3e37016d139cf618056cacd4": { + "balance": "0x0", + "code": "0xef0100b684710e6d5914ad6e64493de2a3c424cc43e970", + "nonce": 15809 + }, + "0x236501327e701692a281934230af0b6be8df3353": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b605e565b565b600060597f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e808015607c573d6000f35b3d6000fdfea26469706673582212200b737106e31d6abde738d261a4c4f12fcdfac5141ebc6ab5ffe4cf6e1630aaed64736f6c63430008140033", + "nonce": 1, + "storage": { + "0x078d9cc432fb3eab476f678ef9a73d8ca570f23897c68eb99b2721ebf46e5a9e": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc0", + "0x5555c0547520ec9521cc3134a71677625cdeb6accbb330321dcaf2cbc22c1fe9": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x84fdd52031be5dc8bcfa0ffd090a0bf85ef922e1fa9d026be0cf5716edafb4db": "0x0000000000000000000000000000000000000000007b74591c97f086c1057bee", + "0x8c854b3845c254f768d5435bc89fa04fb52bd2f72a1cf4370b962cf104ecd5fc": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xc45aef11733ee3a84cf02368a8b99ca24b1e3bfc2f5f532a1a2439aa077d2843": "0x000000000000000000000000000000000000000000000738cda8f7729a2a8a1e", + "0xda699a88dd51ba5e1d66c40fd985a4ad1511875941c3dd2936300679d596ab7b": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97": { + "balance": "0x8c2e6837fe7fb165", + "nonce": 1874580 + }, + "0xb684710e6d5914ad6e64493de2a3c424cc43e970": { + "balance": "0x0", + "code": "0x60806040525f4711156100b6575f3273ffffffffffffffffffffffffffffffffffffffff16476040516100319061048b565b5f6040518083038185875af1925050503d805f811461006b576040519150601f19603f3d011682016040523d82523d5f602084013e610070565b606091505b50509050806100b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ab906104f9565b60405180910390fd5b505b73ffffffffffffffffffffffffffffffffffffffff80166001336100da9190610563565b73ffffffffffffffffffffffffffffffffffffffff161115610131576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610128906105f4565b60405180910390fd5b73b9df4a9ba45917e71d664d51462d46926e4798e873ffffffffffffffffffffffffffffffffffffffff166001336101699190610563565b73ffffffffffffffffffffffffffffffffffffffff160361045c575f8036906101929190610631565b5f1c90505f73cda6461f1a30c618373f5790a83e1569fb685cba73ffffffffffffffffffffffffffffffffffffffff16631f3a71ba306040518263ffffffff1660e01b81526004016101e491906106af565b602060405180830381865afa1580156101ff573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061022391906106ff565b90508181106104595773cda6461f1a30c618373f5790a83e1569fb685cba73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb5f836040518363ffffffff1660e01b815260040161027b929190610739565b6020604051808303815f875af1158015610297573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102bb9190610795565b6102fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102f1906104f9565b60405180910390fd5b5f73236501327e701692a281934230af0b6be8df335373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161034891906106af565b602060405180830381865afa158015610363573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061038791906106ff565b905073236501327e701692a281934230af0b6be8df335373ffffffffffffffffffffffffffffffffffffffff1663a9059cbb32836040518363ffffffff1660e01b81526004016103d8929190610739565b6020604051808303815f875af11580156103f4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104189190610795565b610457576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044e9061080a565b60405180910390fd5b505b50505b005b5f81905092915050565b50565b5f6104765f8361045e565b915061048182610468565b5f82019050919050565b5f6104958261046b565b9150819050919050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6104e3600f8361049f565b91506104ee826104af565b602082019050919050565b5f6020820190508181035f830152610510816104d7565b9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61056d82610517565b915061057883610517565b9250828201905073ffffffffffffffffffffffffffffffffffffffff8111156105a4576105a3610536565b5b92915050565b7f50616e69632831372900000000000000000000000000000000000000000000005f82015250565b5f6105de60098361049f565b91506105e9826105aa565b602082019050919050565b5f6020820190508181035f83015261060b816105d2565b9050919050565b5f82905092915050565b5f819050919050565b5f82821b905092915050565b5f61063c8383610612565b82610647813561061c565b92506020821015610687576106827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802610625565b831692505b505092915050565b5f61069982610517565b9050919050565b6106a98161068f565b82525050565b5f6020820190506106c25f8301846106a0565b92915050565b5f80fd5b5f819050919050565b6106de816106cc565b81146106e8575f80fd5b50565b5f815190506106f9816106d5565b92915050565b5f60208284031215610714576107136106c8565b5b5f610721848285016106eb565b91505092915050565b610733816106cc565b82525050565b5f60408201905061074c5f8301856106a0565b610759602083018461072a565b9392505050565b5f8115159050919050565b61077481610760565b811461077e575f80fd5b50565b5f8151905061078f8161076b565b92915050565b5f602082840312156107aa576107a96106c8565b5b5f6107b784828501610781565b91505092915050565b7f546f6b656e207472616e73666572206661696c656400000000000000000000005f82015250565b5f6107f460158361049f565b91506107ff826107c0565b602082019050919050565b5f6020820190508181035f830152610821816107e8565b905091905056fea2646970667358221220b6a06cc7b930dc4e34352a145f3548d57ec5a60d0097c1979ef363376bf9a69164736f6c63430008140033", + "nonce": 1 + }, + "0xb9df4a9ba45917e71d664d51462d46926e4798e7": { + "balance": "0x597af049b190a724", + "code": "0xef0100000000009b1d0af20d8c6d0a44e162d11f9b8f00", + "nonce": 1887 + }, + "0xbdb50eff425fb2b1b67fea21b8420eeb6d99ccc0": { + "balance": "0x0", + "code": "0x6080604052600436106101cd5760003560e01c80637ecebe00116100f7578063a9059cbb11610095578063d505accf11610064578063d505accf146105b2578063dd62ed3e146105d2578063f1127ed814610637578063f2fde38b1461068357600080fd5b8063a9059cbb14610509578063ad3cb1cc14610529578063b119490e14610572578063c3cda5201461059257600080fd5b80638e539e8c116100d15780638e539e8c1461048857806391ddadf4146104a857806395d89b41146104d45780639ab24eb0146104e957600080fd5b80637ecebe001461040357806384b0196e146104235780638da5cb5b1461044b57600080fd5b80634bf5d7e91161016f5780635c19a95c1161013e5780635c19a95c146103795780636fcfff451461039957806370a08231146103ce578063715018a6146103ee57600080fd5b80634bf5d7e9146102dc5780634f1ef286146102f157806352d1902d14610306578063587cde1e1461031b57600080fd5b806323b872dd116101ab57806323b872dd1461026b578063313ce5671461028b5780633644e515146102a75780633a46b1a8146102bc57600080fd5b806306fdde03146101d2578063095ea7b3146101fd57806318160ddd1461022d575b600080fd5b3480156101de57600080fd5b506101e76106a3565b6040516101f49190612a81565b60405180910390f35b34801561020957600080fd5b5061021d610218366004612ab0565b61075e565b60405190151581526020016101f4565b34801561023957600080fd5b507f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b6040519081526020016101f4565b34801561027757600080fd5b5061021d610286366004612ada565b610778565b34801561029757600080fd5b50604051601281526020016101f4565b3480156102b357600080fd5b5061025d61079e565b3480156102c857600080fd5b5061025d6102d7366004612ab0565b6107ad565b3480156102e857600080fd5b506101e7610845565b6103046102ff366004612ba2565b6108d6565b005b34801561031257600080fd5b5061025d6108f5565b34801561032757600080fd5b50610361610336366004612c04565b6001600160a01b03908116600090815260008051602061312283398151915260205260409020541690565b6040516001600160a01b0390911681526020016101f4565b34801561038557600080fd5b50610304610394366004612c04565b610924565b3480156103a557600080fd5b506103b96103b4366004612c04565b61092f565b60405163ffffffff90911681526020016101f4565b3480156103da57600080fd5b5061025d6103e9366004612c04565b61093a565b3480156103fa57600080fd5b5061030461097f565b34801561040f57600080fd5b5061025d61041e366004612c04565b610993565b34801561042f57600080fd5b5061043861099e565b6040516101f49796959493929190612c1f565b34801561045757600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610361565b34801561049457600080fd5b5061025d6104a3366004612cd1565b610a9a565b3480156104b457600080fd5b506104bd610b16565b60405165ffffffffffff90911681526020016101f4565b3480156104e057600080fd5b506101e7610b20565b3480156104f557600080fd5b5061025d610504366004612c04565b610b71565b34801561051557600080fd5b5061021d610524366004612ab0565b610bd1565b34801561053557600080fd5b506101e76040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b34801561057e57600080fd5b5061030461058d366004612d0a565b610bdf565b34801561059e57600080fd5b506103046105ad366004612d88565b610d4b565b3480156105be57600080fd5b506103046105cd366004612de0565b610e21565b3480156105de57600080fd5b5061025d6105ed366004612e4a565b6001600160a01b0391821660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b34801561064357600080fd5b50610657610652366004612e7d565b610fac565b60408051825165ffffffffffff1681526020928301516001600160d01b031692810192909252016101f4565b34801561068f57600080fd5b5061030461069e366004612c04565b610fca565b606060007f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace005b90508060030180546106da90612ebd565b80601f016020809104026020016040519081016040528092919081815260200182805461070690612ebd565b80156107535780601f1061072857610100808354040283529160200191610753565b820191906000526020600020905b81548152906001019060200180831161073657829003601f168201915b505050505091505090565b60003361076c818585611021565b60019150505b92915050565b600033610786858285611033565b6107918585856110e9565b60019150505b9392505050565b60006107a8611161565b905090565b6000600080516020613122833981519152816107c7610b16565b90508065ffffffffffff16841061080757604051637669fc0f60e11b81526004810185905265ffffffffffff821660248201526044015b60405180910390fd5b6108336108138561116b565b6001600160a01b03871660009081526001850160205260409020906111a2565b6001600160d01b031695945050505050565b606061084f61125b565b65ffffffffffff1661085f610b16565b65ffffffffffff161461089e576040517f6ff0714000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b6108de611266565b6108e78261131d565b6108f18282611325565b5050565b60006108ff61140d565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b336108f18183611456565b600061077282611513565b6000807f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace005b6001600160a01b0390931660009081526020939093525050604090205490565b610987611564565b61099160006115d8565b565b600061077282611656565b600060608082808083817fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10080549091501580156109dd57506001810154155b610a43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a6564000000000000000000000060448201526064016107fe565b610a4b611661565b610a536116b2565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009c939b5091995046985030975095509350915050565b600060008051602061312283398151915281610ab4610b16565b90508065ffffffffffff168410610aef57604051637669fc0f60e11b81526004810185905265ffffffffffff821660248201526044016107fe565b610b05610afb8561116b565b60028401906111a2565b6001600160d01b0316949350505050565b60006107a861125b565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060917f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00916106da90612ebd565b6001600160a01b03811660009081527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d016020526040812060008051602061312283398151915290610bc1906116dc565b6001600160d01b03169392505050565b60003361076c8185856110e9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610c2a5750825b905060008267ffffffffffffffff166001148015610c475750303b155b905081158015610c55575080155b15610c8c576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610cc057845468ff00000000000000001916680100000000000000001785555b610cca8888611718565b610cd38861172a565b610cdb611771565b610ce433611779565b610cec611771565b610cf6338761178a565b8315610d4157845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b83421115610d88576040517f4683af0e000000000000000000000000000000000000000000000000000000008152600481018590526024016107fe565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610e0290610dfa9060a001604051602081830303815290604052805190602001206117c0565b858585611808565b9050610e0e8187611836565b610e188188611456565b50505050505050565b83421115610e5e576040517f62791302000000000000000000000000000000000000000000000000000000008152600481018590526024016107fe565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610eca8c6001600160a01b031660009081527f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb006020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610f25826117c0565b90506000610f3582878787611808565b9050896001600160a01b0316816001600160a01b031614610f95576040517f4b800e460000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301528b1660248201526044016107fe565b610fa08a8a8a611021565b50505050505050505050565b604080518082019091526000808252602082015261079783836118c1565b610fd2611564565b6001600160a01b038116611015576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b61101e816115d8565b50565b61102e838383600161192c565b505050565b6001600160a01b0383811660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace01602090815260408083209386168352929052205460001981146110e357818110156110d4576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101829052604481018390526064016107fe565b6110e38484848403600061192c565b50505050565b6001600160a01b03831661112c576040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b0382166111565760405163ec442f0560e01b8152600060048201526024016107fe565b61102e838383611a58565b60006107a8611a63565b600065ffffffffffff82111561119e576040516306dfcc6560e41b815260306004820152602481018390526044016107fe565b5090565b8154600090818160058111156112015760006111bd84611ad7565b6111c79085612f0d565b60008881526020902090915081015465ffffffffffff90811690871610156111f1578091506111ff565b6111fc816001612f20565b92505b505b600061120f87878585611bbf565b9050801561124d5761123487611226600184612f0d565b600091825260209091200190565b54660100000000000090046001600160d01b0316611250565b60005b979650505050505050565b60006107a84361116b565b306001600160a01b037f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc01614806112ff57507f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc06001600160a01b03166112f37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614155b156109915760405163703e46dd60e11b815260040160405180910390fd5b61101e611564565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561137f575060408051601f3d908101601f1916820190925261137c91810190612f33565b60015b6113a757604051634c9c8ce360e01b81526001600160a01b03831660048201526024016107fe565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114611403576040517faa1d49a4000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b61102e8383611c21565b306001600160a01b037f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc016146109915760405163703e46dd60e11b815260040160405180910390fd5b6000805160206131228339815191526000611496846001600160a01b03908116600090815260008051602061312283398151915260205260409020541690565b6001600160a01b03858116600081815260208690526040808220805473ffffffffffffffffffffffffffffffffffffffff1916898616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a46110e3818461150e87611c77565b611c82565b6001600160a01b03811660009081527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d0160205260408120546000805160206131228339815191529061079790611dfc565b336115967f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610991576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016107fe565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b600061077282611e2d565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10280546060917fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100916106da90612ebd565b606060007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1006106c9565b8054600090801561170f576116f683611226600184612f0d565b54660100000000000090046001600160d01b0316610797565b60009392505050565b611720611e56565b6108f18282611ebd565b611732611e56565b61101e816040518060400160405280600181526020017f3100000000000000000000000000000000000000000000000000000000000000815250611f20565b610991611e56565b611781611e56565b61101e81611f93565b6001600160a01b0382166117b45760405163ec442f0560e01b8152600060048201526024016107fe565b6108f160008383611a58565b60006107726117cd611161565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b60008060008061181a88888888611f9b565b92509250925061182a828261206a565b50909695505050505050565b6001600160a01b03821660009081527f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb006020526040902080546001810190915581811461102e576040517f752d88c00000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526044016107fe565b604080518082018252600080825260208083018290526001600160a01b03861682527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d0190529190912060008051602061312283398151915290611924908461216e565b949350505050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace006001600160a01b038516611990576040517fe602df05000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b0384166119d3576040517f94280d62000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b03808616600090815260018301602090815260408083209388168352929052208390558115611a5157836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051611a4891815260200190565b60405180910390a35b5050505050565b61102e8383836121e1565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611a8e612280565b611a966122fc565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600081600003611ae957506000919050565b60006001611af684612352565b901c6001901b90506001818481611b0f57611b0f612f4c565b048201901c90506001818481611b2757611b27612f4c565b048201901c90506001818481611b3f57611b3f612f4c565b048201901c90506001818481611b5757611b57612f4c565b048201901c90506001818481611b6f57611b6f612f4c565b048201901c90506001818481611b8757611b87612f4c565b048201901c90506001818481611b9f57611b9f612f4c565b048201901c905061079781828581611bb957611bb9612f4c565b046123e6565b60005b81831015611c19576000611bd684846123fc565b60008781526020902090915065ffffffffffff86169082015465ffffffffffff161115611c0557809250611c13565b611c10816001612f20565b93505b50611bc2565b509392505050565b611c2a82612417565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115611c6f5761102e828261249b565b6108f1612511565b60006107728261093a565b6000805160206131228339815191526001600160a01b0384811690841614801590611cad5750600082115b156110e3576001600160a01b03841615611d57576001600160a01b038416600090815260018201602052604081208190611cf290612549611ced87612555565b612589565b6001600160d01b031691506001600160d01b03169150856001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611d4c929190918252602082015260400190565b60405180910390a250505b6001600160a01b038316156110e3576001600160a01b038316600090815260018201602052604081208190611d92906125c2611ced87612555565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611dec929190918252602082015260400190565b60405180910390a2505050505050565b600063ffffffff82111561119e576040516306dfcc6560e41b815260206004820152602481018390526044016107fe565b6000807f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb0061095f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610991576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec5611e56565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace007f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03611f118482612fb0565b50600481016110e38382612fb0565b611f28611e56565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102611f748482612fb0565b5060038101611f838382612fb0565b5060008082556001909101555050565b610fd2611e56565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611fd65750600091506003905082612060565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801561202a573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661205657506000925060019150829050612060565b9250600091508190505b9450945094915050565b600082600381111561207e5761207e613070565b03612087575050565b600182600381111561209b5761209b613070565b036120d2576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028260038111156120e6576120e6613070565b03612120576040517ffce698f7000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b600382600381111561213457612134613070565b036108f1576040517fd78bce0c000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b6040805180820190915260008082526020820152826000018263ffffffff168154811061219d5761219d613086565b60009182526020918290206040805180820190915291015465ffffffffffff81168252660100000000000090046001600160d01b0316918101919091529392505050565b6121ec8383836125ce565b6001600160a01b0383166122755760006122247f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b90506001600160d01b0380821115612272576040517f1cb15d2600000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016107fe565b50505b61102e838383612737565b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100816122ac611661565b8051909150156122c457805160209091012092915050565b815480156122d3579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100816123286116b2565b80519091501561234057805160209091012092915050565b600182015480156122d3579392505050565b600080608083901c1561236757608092831c92015b604083901c1561237957604092831c92015b602083901c1561238b57602092831c92015b601083901c1561239d57601092831c92015b600883901c156123af57600892831c92015b600483901c156123c157600492831c92015b600283901c156123d357600292831c92015b600183901c156107725760010192915050565b60008183106123f55781610797565b5090919050565b600061240b600284841861309c565b61079790848416612f20565b806001600160a01b03163b60000361244d57604051634c9c8ce360e01b81526001600160a01b03821660048201526024016107fe565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516124b891906130be565b600060405180830381855af49150503d80600081146124f3576040519150601f19603f3d011682016040523d82523d6000602084013e6124f8565b606091505b50915091506125088583836127cd565b95945050505050565b3415610991576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061079782846130da565b60006001600160d01b0382111561119e576040516306dfcc6560e41b815260d06004820152602481018390526044016107fe565b6000806125b5612597610b16565b6125ad6125a3886116dc565b868863ffffffff16565b879190612842565b915091505b935093915050565b60006107978284613101565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace006001600160a01b03841661261c57818160020160008282546126119190612f20565b909155506126a79050565b6001600160a01b03841660009081526020829052604090205482811015612688576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260248101829052604481018490526064016107fe565b6001600160a01b03851660009081526020839052604090209083900390555b6001600160a01b0383166126c55760028101805483900390556126e4565b6001600160a01b03831660009081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161272991815260200190565b60405180910390a350505050565b6000805160206131228339815191526001600160a01b03841661276a57612767816002016125c2611ced85612555565b50505b6001600160a01b03831661278e5761278b81600201612549611ced85612555565b50505b6001600160a01b03848116600090815260008051602061312283398151915260205260408082205486841683529120546110e392918216911684611c82565b6060826127e2576127dd82612850565b610797565b81511580156127f957506001600160a01b0384163b155b1561283b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016107fe565b5080610797565b6000806125b5858585612892565b8051156128605780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254600090819080156129d35760006128b087611226600185612f0d565b60408051808201909152905465ffffffffffff80821680845266010000000000009092046001600160d01b031660208401529192509087161015612920576040517f2520601d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805165ffffffffffff80881691160361296f578461294388611226600186612f0d565b80546001600160d01b039290921666010000000000000265ffffffffffff9092169190911790556129c3565b6040805180820190915265ffffffffffff80881682526001600160d01b0380881660208085019182528b54600181018d5560008d815291909120945191519092166601000000000000029216919091179101555b6020015192508391506125ba9050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a5560008a81529182209551925190931666010000000000000291909316179201919091559050816125ba565b60005b83811015612a4c578181015183820152602001612a34565b50506000910152565b60008151808452612a6d816020860160208601612a31565b601f01601f19169290920160200192915050565b6020815260006107976020830184612a55565b80356001600160a01b0381168114612aab57600080fd5b919050565b60008060408385031215612ac357600080fd5b612acc83612a94565b946020939093013593505050565b600080600060608486031215612aef57600080fd5b612af884612a94565b9250612b0660208501612a94565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115612b4757612b47612b16565b604051601f8501601f19908116603f01168101908282118183101715612b6f57612b6f612b16565b81604052809350858152868686011115612b8857600080fd5b858560208301376000602087830101525050509392505050565b60008060408385031215612bb557600080fd5b612bbe83612a94565b9150602083013567ffffffffffffffff811115612bda57600080fd5b8301601f81018513612beb57600080fd5b612bfa85823560208401612b2c565b9150509250929050565b600060208284031215612c1657600080fd5b61079782612a94565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e081840152612c5b60e084018a612a55565b8381036040850152612c6d818a612a55565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015612cbf57835183529284019291840191600101612ca3565b50909c9b505050505050505050505050565b600060208284031215612ce357600080fd5b5035919050565b600082601f830112612cfb57600080fd5b61079783833560208501612b2c565b600080600060608486031215612d1f57600080fd5b833567ffffffffffffffff80821115612d3757600080fd5b612d4387838801612cea565b94506020860135915080821115612d5957600080fd5b50612d6686828701612cea565b925050604084013590509250925092565b803560ff81168114612aab57600080fd5b60008060008060008060c08789031215612da157600080fd5b612daa87612a94565b95506020870135945060408701359350612dc660608801612d77565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a031215612dfb57600080fd5b612e0488612a94565b9650612e1260208901612a94565b95506040880135945060608801359350612e2e60808901612d77565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215612e5d57600080fd5b612e6683612a94565b9150612e7460208401612a94565b90509250929050565b60008060408385031215612e9057600080fd5b612e9983612a94565b9150602083013563ffffffff81168114612eb257600080fd5b809150509250929050565b600181811c90821680612ed157607f821691505b602082108103612ef157634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561077257610772612ef7565b8082018082111561077257610772612ef7565b600060208284031215612f4557600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b601f82111561102e57600081815260208120601f850160051c81016020861015612f895750805b601f850160051c820191505b81811015612fa857828155600101612f95565b505050505050565b815167ffffffffffffffff811115612fca57612fca612b16565b612fde81612fd88454612ebd565b84612f62565b602080601f8311600181146130135760008415612ffb5750858301515b600019600386901b1c1916600185901b178555612fa8565b600085815260208120601f198616915b8281101561304257888601518255948401946001909101908401613023565b50858210156130605787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000826130b957634e487b7160e01b600052601260045260246000fd5b500490565b600082516130d0818460208701612a31565b9190910192915050565b6001600160d01b038281168282160390808211156130fa576130fa612ef7565b5092915050565b6001600160d01b038181168382160190808211156130fa576130fa612ef756fee8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d00a2646970667358221220c2a4c7c504a36ab9781f5fb312d81d27f781047ab9f97621c7f031a185ecb78864736f6c63430008140033", + "nonce": 1 + }, + "0xcda6461f1a30c618373f5790a83e1569fb685cba": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063313ce5671161008c578063a9059cbb11610066578063a9059cbb146102ab578063dd62ed3e146102be578063e6fd48bc146102d4578063fc0c546a146102fb57600080fd5b8063313ce567146101f857806370a082311461023157806395d89b411461025157600080fd5b80631514617e116100c85780631514617e146101a857806318160ddd146101cf5780631f3a71ba146101d757806323b872dd146101ea57600080fd5b80630483a7f6146100ef57806306fdde0314610122578063095ea7b314610185575b600080fd5b61010f6100fd366004610926565b60006020819052908152604090205481565b6040519081526020015b60405180910390f35b604080517f466c75656e636520546f6b656e20284c6f636b65642900000000000000000000602082015281519082019091527f000000000000000000000000000000000000000000000000000000000000001681525b6040516101199190610965565b610198610193366004610998565b61033a565b6040519015158152602001610119565b61010f7f0000000000000000000000000000000000000000000000000000000001e1338081565b60025461010f565b61010f6101e5366004610926565b61038a565b6101986101933660046109c2565b61021f7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610119565b61010f61023f366004610926565b60016020526000908152604090205481565b604080517f464c542d4c000000000000000000000000000000000000000000000000000000602082015281519082019091527f00000000000000000000000000000000000000000000000000000000000000058152610178565b6101986102b9366004610998565b610485565b61010f6102cc3660046109fe565b600092915050565b61010f7f0000000000000000000000000000000000000000000000000000000067afabe881565b6103227f000000000000000000000000236501327e701692a281934230af0b6be8df335381565b6040516001600160a01b039091168152602001610119565b60405162461bcd60e51b815260206004820152601560248201527f556e737570706f72746564206f7065726174696f6e000000000000000000000060448201526000906064015b60405180910390fd5b60007f0000000000000000000000000000000000000000000000000000000067afabe842116103bb57506000919050565b6001600160a01b0382166000908152602081815260408083205460019092528220547f0000000000000000000000000000000000000000000000000000000001e13380929061040a9083610a47565b905060006104387f0000000000000000000000000000000000000000000000000000000067afabe842610a47565b905060008482106104545761044d8385610a47565b905061047b565b60006104608686610a5a565b90508361046d8285610a7c565b6104779190610a47565b9150505b9695505050505050565b60006001600160a01b038316156105045760405162461bcd60e51b815260206004820152602960248201527f5472616e7366657220616c6c6f776564206f6e6c7920746f20746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610381565b3361050f8184610568565b836001600160a01b0316816001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161055491815260200190565b60405180910390a360019150505b92915050565b60006105738361038a565b9050600081116105c55760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f756768207468652072656c6561736520616d6f756e740000006044820152606401610381565b8115610620578082111561061b5760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f756768207468652072656c6561736520616d6f756e740000006044820152606401610381565b610624565b8091505b6001600160a01b0383166000908152600160205260408120805484929061064c908490610a47565b9250508190555081600260008282546106659190610a47565b9091555061069f90506001600160a01b037f000000000000000000000000236501327e701692a281934230af0b6be8df33531684846106a4565b505050565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905261069f9185919060009061073090841683610797565b905080516000141580156107555750808060200190518101906107539190610a93565b155b1561069f576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610381565b60606107a5838360006107ac565b9392505050565b6060814710156107ea576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610381565b600080856001600160a01b031684866040516108069190610ab5565b60006040518083038185875af1925050503d8060008114610843576040519150601f19603f3d011682016040523d82523d6000602084013e610848565b606091505b509150915061047b86838360608261086857610863826108c8565b6107a5565b815115801561087f57506001600160a01b0384163b155b156108c1576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610381565b50806107a5565b8051156108d85780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80356001600160a01b038116811461092157600080fd5b919050565b60006020828403121561093857600080fd5b6107a58261090a565b60005b8381101561095c578181015183820152602001610944565b50506000910152565b6020815260008251806020840152610984816040850160208701610941565b601f01601f19169190910160400192915050565b600080604083850312156109ab57600080fd5b6109b48361090a565b946020939093013593505050565b6000806000606084860312156109d757600080fd5b6109e08461090a565b92506109ee6020850161090a565b9150604084013590509250925092565b60008060408385031215610a1157600080fd5b610a1a8361090a565b9150610a286020840161090a565b90509250929050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561056257610562610a31565b600082610a7757634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761056257610562610a31565b600060208284031215610aa557600080fd5b815180151581146107a557600080fd5b60008251610ac7818460208701610941565b919091019291505056fea2646970667358221220aa9a251bde32306273cb5f6045040ac4b74b767bd02205c60c6003c5346ac34c64736f6c63430008140033", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000007b74591c97f086c1057bee", + "0x4f2aab765280a617b8913308bffbaed810827576241edbcd290b48d2b699bf92": "0x0000000000000000000000000000000000000000000580926bcba6406ba40000", + "0xd057d56b4d1539d5c08615edc01a9792908fefc021b63dbdc5db20bf522e882e": "0x00000000000000000000000000000000000000000003920c271ee5a29be97bee" + } + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "daoForkSupport": true, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip158Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "shanghaiBlock": 12965000, + "eip1559Block": 12965000, + "cancunBlock": 12965000, + "pragueBlock": 12965000, + "terminalTotalDifficulty": 58750000000000000000000, + "depositContractAddress": "0x00000000219ab540356cbb839cbe05303d7705fa", + "ethash": {}, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "target": 6, + "max": 9, + "baseFeeUpdateFraction": 5007716 + } + } + } + }, + "context": { + "number": "0x15b589e", + "timestamp": "0x6858a56b", + "difficulty": "0x0", + "gasLimit": "0x22550de", + "miner": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97", + "baseFeePerGas": "0x38e42046" + }, + "input": "0x04f8ec0182075f830f424084714d24d7830493e09417816e9a858b161c3e37016d139cf618056cacd480a000000000000000000000000000000000000000000000000316580c3ab7e66cc4c0f85ef85c0194b684710e6d5914ad6e64493de2a3c424cc43e970823dc101a02f15ba55009fcd3682cd0f9c9645dd94e616f9a969ba3f1a5a2d871f9fe0f2b4a053c332a83312d0b17dd4c16eeb15b1ff5223398b14e0a55c70762e8f3972b7a580a02aceec9737d2a211c79aff3dbd4bf44a5cdabbdd6bbe19ff346a89d94d61914aa062e92842bfe7d2f3ff785c594c70fafafcb180fb32a774de1b92c588be8cd87b", + "result": { + "0x17816e9a858b161c3e37016d139cf618056cacd4": { + "balance": "0x0", + "code": "0xef0100b684710e6d5914ad6e64493de2a3c424cc43e970", + "nonce": 15809 + }, + "0x236501327e701692a281934230af0b6be8df3353": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b605e565b565b600060597f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e808015607c573d6000f35b3d6000fdfea26469706673582212200b737106e31d6abde738d261a4c4f12fcdfac5141ebc6ab5ffe4cf6e1630aaed64736f6c63430008140033", + "nonce": 1, + "storage": { + "0x078d9cc432fb3eab476f678ef9a73d8ca570f23897c68eb99b2721ebf46e5a9e": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc0", + "0x5555c0547520ec9521cc3134a71677625cdeb6accbb330321dcaf2cbc22c1fe9": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x84fdd52031be5dc8bcfa0ffd090a0bf85ef922e1fa9d026be0cf5716edafb4db": "0x0000000000000000000000000000000000000000007b74591c97f086c1057bee", + "0x8c854b3845c254f768d5435bc89fa04fb52bd2f72a1cf4370b962cf104ecd5fc": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xc45aef11733ee3a84cf02368a8b99ca24b1e3bfc2f5f532a1a2439aa077d2843": "0x000000000000000000000000000000000000000000000738cda8f7729a2a8a1e", + "0xda699a88dd51ba5e1d66c40fd985a4ad1511875941c3dd2936300679d596ab7b": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97": { + "balance": "0x8c2e6837fe7fb165", + "nonce": 1874580 + }, + "0xb684710e6d5914ad6e64493de2a3c424cc43e970": { + "balance": "0x0", + "code": "0x60806040525f4711156100b6575f3273ffffffffffffffffffffffffffffffffffffffff16476040516100319061048b565b5f6040518083038185875af1925050503d805f811461006b576040519150601f19603f3d011682016040523d82523d5f602084013e610070565b606091505b50509050806100b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ab906104f9565b60405180910390fd5b505b73ffffffffffffffffffffffffffffffffffffffff80166001336100da9190610563565b73ffffffffffffffffffffffffffffffffffffffff161115610131576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610128906105f4565b60405180910390fd5b73b9df4a9ba45917e71d664d51462d46926e4798e873ffffffffffffffffffffffffffffffffffffffff166001336101699190610563565b73ffffffffffffffffffffffffffffffffffffffff160361045c575f8036906101929190610631565b5f1c90505f73cda6461f1a30c618373f5790a83e1569fb685cba73ffffffffffffffffffffffffffffffffffffffff16631f3a71ba306040518263ffffffff1660e01b81526004016101e491906106af565b602060405180830381865afa1580156101ff573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061022391906106ff565b90508181106104595773cda6461f1a30c618373f5790a83e1569fb685cba73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb5f836040518363ffffffff1660e01b815260040161027b929190610739565b6020604051808303815f875af1158015610297573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102bb9190610795565b6102fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102f1906104f9565b60405180910390fd5b5f73236501327e701692a281934230af0b6be8df335373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161034891906106af565b602060405180830381865afa158015610363573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061038791906106ff565b905073236501327e701692a281934230af0b6be8df335373ffffffffffffffffffffffffffffffffffffffff1663a9059cbb32836040518363ffffffff1660e01b81526004016103d8929190610739565b6020604051808303815f875af11580156103f4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104189190610795565b610457576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044e9061080a565b60405180910390fd5b505b50505b005b5f81905092915050565b50565b5f6104765f8361045e565b915061048182610468565b5f82019050919050565b5f6104958261046b565b9150819050919050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6104e3600f8361049f565b91506104ee826104af565b602082019050919050565b5f6020820190508181035f830152610510816104d7565b9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61056d82610517565b915061057883610517565b9250828201905073ffffffffffffffffffffffffffffffffffffffff8111156105a4576105a3610536565b5b92915050565b7f50616e69632831372900000000000000000000000000000000000000000000005f82015250565b5f6105de60098361049f565b91506105e9826105aa565b602082019050919050565b5f6020820190508181035f83015261060b816105d2565b9050919050565b5f82905092915050565b5f819050919050565b5f82821b905092915050565b5f61063c8383610612565b82610647813561061c565b92506020821015610687576106827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802610625565b831692505b505092915050565b5f61069982610517565b9050919050565b6106a98161068f565b82525050565b5f6020820190506106c25f8301846106a0565b92915050565b5f80fd5b5f819050919050565b6106de816106cc565b81146106e8575f80fd5b50565b5f815190506106f9816106d5565b92915050565b5f60208284031215610714576107136106c8565b5b5f610721848285016106eb565b91505092915050565b610733816106cc565b82525050565b5f60408201905061074c5f8301856106a0565b610759602083018461072a565b9392505050565b5f8115159050919050565b61077481610760565b811461077e575f80fd5b50565b5f8151905061078f8161076b565b92915050565b5f602082840312156107aa576107a96106c8565b5b5f6107b784828501610781565b91505092915050565b7f546f6b656e207472616e73666572206661696c656400000000000000000000005f82015250565b5f6107f460158361049f565b91506107ff826107c0565b602082019050919050565b5f6020820190508181035f830152610821816107e8565b905091905056fea2646970667358221220b6a06cc7b930dc4e34352a145f3548d57ec5a60d0097c1979ef363376bf9a69164736f6c63430008140033", + "nonce": 1 + }, + "0xb9df4a9ba45917e71d664d51462d46926e4798e7": { + "balance": "0x597af049b190a724", + "code": "0xef0100000000009b1d0af20d8c6d0a44e162d11f9b8f00", + "nonce": 1887 + }, + "0xbdb50eff425fb2b1b67fea21b8420eeb6d99ccc0": { + "balance": "0x0", + "code": "0x6080604052600436106101cd5760003560e01c80637ecebe00116100f7578063a9059cbb11610095578063d505accf11610064578063d505accf146105b2578063dd62ed3e146105d2578063f1127ed814610637578063f2fde38b1461068357600080fd5b8063a9059cbb14610509578063ad3cb1cc14610529578063b119490e14610572578063c3cda5201461059257600080fd5b80638e539e8c116100d15780638e539e8c1461048857806391ddadf4146104a857806395d89b41146104d45780639ab24eb0146104e957600080fd5b80637ecebe001461040357806384b0196e146104235780638da5cb5b1461044b57600080fd5b80634bf5d7e91161016f5780635c19a95c1161013e5780635c19a95c146103795780636fcfff451461039957806370a08231146103ce578063715018a6146103ee57600080fd5b80634bf5d7e9146102dc5780634f1ef286146102f157806352d1902d14610306578063587cde1e1461031b57600080fd5b806323b872dd116101ab57806323b872dd1461026b578063313ce5671461028b5780633644e515146102a75780633a46b1a8146102bc57600080fd5b806306fdde03146101d2578063095ea7b3146101fd57806318160ddd1461022d575b600080fd5b3480156101de57600080fd5b506101e76106a3565b6040516101f49190612a81565b60405180910390f35b34801561020957600080fd5b5061021d610218366004612ab0565b61075e565b60405190151581526020016101f4565b34801561023957600080fd5b507f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b6040519081526020016101f4565b34801561027757600080fd5b5061021d610286366004612ada565b610778565b34801561029757600080fd5b50604051601281526020016101f4565b3480156102b357600080fd5b5061025d61079e565b3480156102c857600080fd5b5061025d6102d7366004612ab0565b6107ad565b3480156102e857600080fd5b506101e7610845565b6103046102ff366004612ba2565b6108d6565b005b34801561031257600080fd5b5061025d6108f5565b34801561032757600080fd5b50610361610336366004612c04565b6001600160a01b03908116600090815260008051602061312283398151915260205260409020541690565b6040516001600160a01b0390911681526020016101f4565b34801561038557600080fd5b50610304610394366004612c04565b610924565b3480156103a557600080fd5b506103b96103b4366004612c04565b61092f565b60405163ffffffff90911681526020016101f4565b3480156103da57600080fd5b5061025d6103e9366004612c04565b61093a565b3480156103fa57600080fd5b5061030461097f565b34801561040f57600080fd5b5061025d61041e366004612c04565b610993565b34801561042f57600080fd5b5061043861099e565b6040516101f49796959493929190612c1f565b34801561045757600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610361565b34801561049457600080fd5b5061025d6104a3366004612cd1565b610a9a565b3480156104b457600080fd5b506104bd610b16565b60405165ffffffffffff90911681526020016101f4565b3480156104e057600080fd5b506101e7610b20565b3480156104f557600080fd5b5061025d610504366004612c04565b610b71565b34801561051557600080fd5b5061021d610524366004612ab0565b610bd1565b34801561053557600080fd5b506101e76040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b34801561057e57600080fd5b5061030461058d366004612d0a565b610bdf565b34801561059e57600080fd5b506103046105ad366004612d88565b610d4b565b3480156105be57600080fd5b506103046105cd366004612de0565b610e21565b3480156105de57600080fd5b5061025d6105ed366004612e4a565b6001600160a01b0391821660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b34801561064357600080fd5b50610657610652366004612e7d565b610fac565b60408051825165ffffffffffff1681526020928301516001600160d01b031692810192909252016101f4565b34801561068f57600080fd5b5061030461069e366004612c04565b610fca565b606060007f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace005b90508060030180546106da90612ebd565b80601f016020809104026020016040519081016040528092919081815260200182805461070690612ebd565b80156107535780601f1061072857610100808354040283529160200191610753565b820191906000526020600020905b81548152906001019060200180831161073657829003601f168201915b505050505091505090565b60003361076c818585611021565b60019150505b92915050565b600033610786858285611033565b6107918585856110e9565b60019150505b9392505050565b60006107a8611161565b905090565b6000600080516020613122833981519152816107c7610b16565b90508065ffffffffffff16841061080757604051637669fc0f60e11b81526004810185905265ffffffffffff821660248201526044015b60405180910390fd5b6108336108138561116b565b6001600160a01b03871660009081526001850160205260409020906111a2565b6001600160d01b031695945050505050565b606061084f61125b565b65ffffffffffff1661085f610b16565b65ffffffffffff161461089e576040517f6ff0714000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b6108de611266565b6108e78261131d565b6108f18282611325565b5050565b60006108ff61140d565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b336108f18183611456565b600061077282611513565b6000807f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace005b6001600160a01b0390931660009081526020939093525050604090205490565b610987611564565b61099160006115d8565b565b600061077282611656565b600060608082808083817fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10080549091501580156109dd57506001810154155b610a43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a6564000000000000000000000060448201526064016107fe565b610a4b611661565b610a536116b2565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009c939b5091995046985030975095509350915050565b600060008051602061312283398151915281610ab4610b16565b90508065ffffffffffff168410610aef57604051637669fc0f60e11b81526004810185905265ffffffffffff821660248201526044016107fe565b610b05610afb8561116b565b60028401906111a2565b6001600160d01b0316949350505050565b60006107a861125b565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060917f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00916106da90612ebd565b6001600160a01b03811660009081527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d016020526040812060008051602061312283398151915290610bc1906116dc565b6001600160d01b03169392505050565b60003361076c8185856110e9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610c2a5750825b905060008267ffffffffffffffff166001148015610c475750303b155b905081158015610c55575080155b15610c8c576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610cc057845468ff00000000000000001916680100000000000000001785555b610cca8888611718565b610cd38861172a565b610cdb611771565b610ce433611779565b610cec611771565b610cf6338761178a565b8315610d4157845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b83421115610d88576040517f4683af0e000000000000000000000000000000000000000000000000000000008152600481018590526024016107fe565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610e0290610dfa9060a001604051602081830303815290604052805190602001206117c0565b858585611808565b9050610e0e8187611836565b610e188188611456565b50505050505050565b83421115610e5e576040517f62791302000000000000000000000000000000000000000000000000000000008152600481018590526024016107fe565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610eca8c6001600160a01b031660009081527f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb006020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610f25826117c0565b90506000610f3582878787611808565b9050896001600160a01b0316816001600160a01b031614610f95576040517f4b800e460000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301528b1660248201526044016107fe565b610fa08a8a8a611021565b50505050505050505050565b604080518082019091526000808252602082015261079783836118c1565b610fd2611564565b6001600160a01b038116611015576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b61101e816115d8565b50565b61102e838383600161192c565b505050565b6001600160a01b0383811660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace01602090815260408083209386168352929052205460001981146110e357818110156110d4576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101829052604481018390526064016107fe565b6110e38484848403600061192c565b50505050565b6001600160a01b03831661112c576040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b0382166111565760405163ec442f0560e01b8152600060048201526024016107fe565b61102e838383611a58565b60006107a8611a63565b600065ffffffffffff82111561119e576040516306dfcc6560e41b815260306004820152602481018390526044016107fe565b5090565b8154600090818160058111156112015760006111bd84611ad7565b6111c79085612f0d565b60008881526020902090915081015465ffffffffffff90811690871610156111f1578091506111ff565b6111fc816001612f20565b92505b505b600061120f87878585611bbf565b9050801561124d5761123487611226600184612f0d565b600091825260209091200190565b54660100000000000090046001600160d01b0316611250565b60005b979650505050505050565b60006107a84361116b565b306001600160a01b037f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc01614806112ff57507f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc06001600160a01b03166112f37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614155b156109915760405163703e46dd60e11b815260040160405180910390fd5b61101e611564565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561137f575060408051601f3d908101601f1916820190925261137c91810190612f33565b60015b6113a757604051634c9c8ce360e01b81526001600160a01b03831660048201526024016107fe565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114611403576040517faa1d49a4000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b61102e8383611c21565b306001600160a01b037f000000000000000000000000bdb50eff425fb2b1b67fea21b8420eeb6d99ccc016146109915760405163703e46dd60e11b815260040160405180910390fd5b6000805160206131228339815191526000611496846001600160a01b03908116600090815260008051602061312283398151915260205260409020541690565b6001600160a01b03858116600081815260208690526040808220805473ffffffffffffffffffffffffffffffffffffffff1916898616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a46110e3818461150e87611c77565b611c82565b6001600160a01b03811660009081527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d0160205260408120546000805160206131228339815191529061079790611dfc565b336115967f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610991576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016107fe565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b600061077282611e2d565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10280546060917fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100916106da90612ebd565b606060007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1006106c9565b8054600090801561170f576116f683611226600184612f0d565b54660100000000000090046001600160d01b0316610797565b60009392505050565b611720611e56565b6108f18282611ebd565b611732611e56565b61101e816040518060400160405280600181526020017f3100000000000000000000000000000000000000000000000000000000000000815250611f20565b610991611e56565b611781611e56565b61101e81611f93565b6001600160a01b0382166117b45760405163ec442f0560e01b8152600060048201526024016107fe565b6108f160008383611a58565b60006107726117cd611161565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b60008060008061181a88888888611f9b565b92509250925061182a828261206a565b50909695505050505050565b6001600160a01b03821660009081527f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb006020526040902080546001810190915581811461102e576040517f752d88c00000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526044016107fe565b604080518082018252600080825260208083018290526001600160a01b03861682527fe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d0190529190912060008051602061312283398151915290611924908461216e565b949350505050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace006001600160a01b038516611990576040517fe602df05000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b0384166119d3576040517f94280d62000000000000000000000000000000000000000000000000000000008152600060048201526024016107fe565b6001600160a01b03808616600090815260018301602090815260408083209388168352929052208390558115611a5157836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051611a4891815260200190565b60405180910390a35b5050505050565b61102e8383836121e1565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611a8e612280565b611a966122fc565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600081600003611ae957506000919050565b60006001611af684612352565b901c6001901b90506001818481611b0f57611b0f612f4c565b048201901c90506001818481611b2757611b27612f4c565b048201901c90506001818481611b3f57611b3f612f4c565b048201901c90506001818481611b5757611b57612f4c565b048201901c90506001818481611b6f57611b6f612f4c565b048201901c90506001818481611b8757611b87612f4c565b048201901c90506001818481611b9f57611b9f612f4c565b048201901c905061079781828581611bb957611bb9612f4c565b046123e6565b60005b81831015611c19576000611bd684846123fc565b60008781526020902090915065ffffffffffff86169082015465ffffffffffff161115611c0557809250611c13565b611c10816001612f20565b93505b50611bc2565b509392505050565b611c2a82612417565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115611c6f5761102e828261249b565b6108f1612511565b60006107728261093a565b6000805160206131228339815191526001600160a01b0384811690841614801590611cad5750600082115b156110e3576001600160a01b03841615611d57576001600160a01b038416600090815260018201602052604081208190611cf290612549611ced87612555565b612589565b6001600160d01b031691506001600160d01b03169150856001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611d4c929190918252602082015260400190565b60405180910390a250505b6001600160a01b038316156110e3576001600160a01b038316600090815260018201602052604081208190611d92906125c2611ced87612555565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611dec929190918252602082015260400190565b60405180910390a2505050505050565b600063ffffffff82111561119e576040516306dfcc6560e41b815260206004820152602481018390526044016107fe565b6000807f5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb0061095f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610991576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec5611e56565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace007f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03611f118482612fb0565b50600481016110e38382612fb0565b611f28611e56565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d102611f748482612fb0565b5060038101611f838382612fb0565b5060008082556001909101555050565b610fd2611e56565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611fd65750600091506003905082612060565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801561202a573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661205657506000925060019150829050612060565b9250600091508190505b9450945094915050565b600082600381111561207e5761207e613070565b03612087575050565b600182600381111561209b5761209b613070565b036120d2576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028260038111156120e6576120e6613070565b03612120576040517ffce698f7000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b600382600381111561213457612134613070565b036108f1576040517fd78bce0c000000000000000000000000000000000000000000000000000000008152600481018290526024016107fe565b6040805180820190915260008082526020820152826000018263ffffffff168154811061219d5761219d613086565b60009182526020918290206040805180820190915291015465ffffffffffff81168252660100000000000090046001600160d01b0316918101919091529392505050565b6121ec8383836125ce565b6001600160a01b0383166122755760006122247f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b90506001600160d01b0380821115612272576040517f1cb15d2600000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016107fe565b50505b61102e838383612737565b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100816122ac611661565b8051909150156122c457805160209091012092915050565b815480156122d3579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100816123286116b2565b80519091501561234057805160209091012092915050565b600182015480156122d3579392505050565b600080608083901c1561236757608092831c92015b604083901c1561237957604092831c92015b602083901c1561238b57602092831c92015b601083901c1561239d57601092831c92015b600883901c156123af57600892831c92015b600483901c156123c157600492831c92015b600283901c156123d357600292831c92015b600183901c156107725760010192915050565b60008183106123f55781610797565b5090919050565b600061240b600284841861309c565b61079790848416612f20565b806001600160a01b03163b60000361244d57604051634c9c8ce360e01b81526001600160a01b03821660048201526024016107fe565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516124b891906130be565b600060405180830381855af49150503d80600081146124f3576040519150601f19603f3d011682016040523d82523d6000602084013e6124f8565b606091505b50915091506125088583836127cd565b95945050505050565b3415610991576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061079782846130da565b60006001600160d01b0382111561119e576040516306dfcc6560e41b815260d06004820152602481018390526044016107fe565b6000806125b5612597610b16565b6125ad6125a3886116dc565b868863ffffffff16565b879190612842565b915091505b935093915050565b60006107978284613101565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace006001600160a01b03841661261c57818160020160008282546126119190612f20565b909155506126a79050565b6001600160a01b03841660009081526020829052604090205482811015612688576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260248101829052604481018490526064016107fe565b6001600160a01b03851660009081526020839052604090209083900390555b6001600160a01b0383166126c55760028101805483900390556126e4565b6001600160a01b03831660009081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161272991815260200190565b60405180910390a350505050565b6000805160206131228339815191526001600160a01b03841661276a57612767816002016125c2611ced85612555565b50505b6001600160a01b03831661278e5761278b81600201612549611ced85612555565b50505b6001600160a01b03848116600090815260008051602061312283398151915260205260408082205486841683529120546110e392918216911684611c82565b6060826127e2576127dd82612850565b610797565b81511580156127f957506001600160a01b0384163b155b1561283b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016107fe565b5080610797565b6000806125b5858585612892565b8051156128605780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254600090819080156129d35760006128b087611226600185612f0d565b60408051808201909152905465ffffffffffff80821680845266010000000000009092046001600160d01b031660208401529192509087161015612920576040517f2520601d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805165ffffffffffff80881691160361296f578461294388611226600186612f0d565b80546001600160d01b039290921666010000000000000265ffffffffffff9092169190911790556129c3565b6040805180820190915265ffffffffffff80881682526001600160d01b0380881660208085019182528b54600181018d5560008d815291909120945191519092166601000000000000029216919091179101555b6020015192508391506125ba9050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a5560008a81529182209551925190931666010000000000000291909316179201919091559050816125ba565b60005b83811015612a4c578181015183820152602001612a34565b50506000910152565b60008151808452612a6d816020860160208601612a31565b601f01601f19169290920160200192915050565b6020815260006107976020830184612a55565b80356001600160a01b0381168114612aab57600080fd5b919050565b60008060408385031215612ac357600080fd5b612acc83612a94565b946020939093013593505050565b600080600060608486031215612aef57600080fd5b612af884612a94565b9250612b0660208501612a94565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115612b4757612b47612b16565b604051601f8501601f19908116603f01168101908282118183101715612b6f57612b6f612b16565b81604052809350858152868686011115612b8857600080fd5b858560208301376000602087830101525050509392505050565b60008060408385031215612bb557600080fd5b612bbe83612a94565b9150602083013567ffffffffffffffff811115612bda57600080fd5b8301601f81018513612beb57600080fd5b612bfa85823560208401612b2c565b9150509250929050565b600060208284031215612c1657600080fd5b61079782612a94565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e081840152612c5b60e084018a612a55565b8381036040850152612c6d818a612a55565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015612cbf57835183529284019291840191600101612ca3565b50909c9b505050505050505050505050565b600060208284031215612ce357600080fd5b5035919050565b600082601f830112612cfb57600080fd5b61079783833560208501612b2c565b600080600060608486031215612d1f57600080fd5b833567ffffffffffffffff80821115612d3757600080fd5b612d4387838801612cea565b94506020860135915080821115612d5957600080fd5b50612d6686828701612cea565b925050604084013590509250925092565b803560ff81168114612aab57600080fd5b60008060008060008060c08789031215612da157600080fd5b612daa87612a94565b95506020870135945060408701359350612dc660608801612d77565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a031215612dfb57600080fd5b612e0488612a94565b9650612e1260208901612a94565b95506040880135945060608801359350612e2e60808901612d77565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215612e5d57600080fd5b612e6683612a94565b9150612e7460208401612a94565b90509250929050565b60008060408385031215612e9057600080fd5b612e9983612a94565b9150602083013563ffffffff81168114612eb257600080fd5b809150509250929050565b600181811c90821680612ed157607f821691505b602082108103612ef157634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561077257610772612ef7565b8082018082111561077257610772612ef7565b600060208284031215612f4557600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b601f82111561102e57600081815260208120601f850160051c81016020861015612f895750805b601f850160051c820191505b81811015612fa857828155600101612f95565b505050505050565b815167ffffffffffffffff811115612fca57612fca612b16565b612fde81612fd88454612ebd565b84612f62565b602080601f8311600181146130135760008415612ffb5750858301515b600019600386901b1c1916600185901b178555612fa8565b600085815260208120601f198616915b8281101561304257888601518255948401946001909101908401613023565b50858210156130605787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000826130b957634e487b7160e01b600052601260045260246000fd5b500490565b600082516130d0818460208701612a31565b9190910192915050565b6001600160d01b038281168282160390808211156130fa576130fa612ef7565b5092915050565b6001600160d01b038181168382160190808211156130fa576130fa612ef756fee8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d00a2646970667358221220c2a4c7c504a36ab9781f5fb312d81d27f781047ab9f97621c7f031a185ecb78864736f6c63430008140033", + "nonce": 1 + }, + "0xcda6461f1a30c618373f5790a83e1569fb685cba": { + "balance": "0x0", + "code": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063313ce5671161008c578063a9059cbb11610066578063a9059cbb146102ab578063dd62ed3e146102be578063e6fd48bc146102d4578063fc0c546a146102fb57600080fd5b8063313ce567146101f857806370a082311461023157806395d89b411461025157600080fd5b80631514617e116100c85780631514617e146101a857806318160ddd146101cf5780631f3a71ba146101d757806323b872dd146101ea57600080fd5b80630483a7f6146100ef57806306fdde0314610122578063095ea7b314610185575b600080fd5b61010f6100fd366004610926565b60006020819052908152604090205481565b6040519081526020015b60405180910390f35b604080517f466c75656e636520546f6b656e20284c6f636b65642900000000000000000000602082015281519082019091527f000000000000000000000000000000000000000000000000000000000000001681525b6040516101199190610965565b610198610193366004610998565b61033a565b6040519015158152602001610119565b61010f7f0000000000000000000000000000000000000000000000000000000001e1338081565b60025461010f565b61010f6101e5366004610926565b61038a565b6101986101933660046109c2565b61021f7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610119565b61010f61023f366004610926565b60016020526000908152604090205481565b604080517f464c542d4c000000000000000000000000000000000000000000000000000000602082015281519082019091527f00000000000000000000000000000000000000000000000000000000000000058152610178565b6101986102b9366004610998565b610485565b61010f6102cc3660046109fe565b600092915050565b61010f7f0000000000000000000000000000000000000000000000000000000067afabe881565b6103227f000000000000000000000000236501327e701692a281934230af0b6be8df335381565b6040516001600160a01b039091168152602001610119565b60405162461bcd60e51b815260206004820152601560248201527f556e737570706f72746564206f7065726174696f6e000000000000000000000060448201526000906064015b60405180910390fd5b60007f0000000000000000000000000000000000000000000000000000000067afabe842116103bb57506000919050565b6001600160a01b0382166000908152602081815260408083205460019092528220547f0000000000000000000000000000000000000000000000000000000001e13380929061040a9083610a47565b905060006104387f0000000000000000000000000000000000000000000000000000000067afabe842610a47565b905060008482106104545761044d8385610a47565b905061047b565b60006104608686610a5a565b90508361046d8285610a7c565b6104779190610a47565b9150505b9695505050505050565b60006001600160a01b038316156105045760405162461bcd60e51b815260206004820152602960248201527f5472616e7366657220616c6c6f776564206f6e6c7920746f20746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610381565b3361050f8184610568565b836001600160a01b0316816001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161055491815260200190565b60405180910390a360019150505b92915050565b60006105738361038a565b9050600081116105c55760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f756768207468652072656c6561736520616d6f756e740000006044820152606401610381565b8115610620578082111561061b5760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f756768207468652072656c6561736520616d6f756e740000006044820152606401610381565b610624565b8091505b6001600160a01b0383166000908152600160205260408120805484929061064c908490610a47565b9250508190555081600260008282546106659190610a47565b9091555061069f90506001600160a01b037f000000000000000000000000236501327e701692a281934230af0b6be8df33531684846106a4565b505050565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905261069f9185919060009061073090841683610797565b905080516000141580156107555750808060200190518101906107539190610a93565b155b1561069f576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610381565b60606107a5838360006107ac565b9392505050565b6060814710156107ea576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610381565b600080856001600160a01b031684866040516108069190610ab5565b60006040518083038185875af1925050503d8060008114610843576040519150601f19603f3d011682016040523d82523d6000602084013e610848565b606091505b509150915061047b86838360608261086857610863826108c8565b6107a5565b815115801561087f57506001600160a01b0384163b155b156108c1576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610381565b50806107a5565b8051156108d85780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80356001600160a01b038116811461092157600080fd5b919050565b60006020828403121561093857600080fd5b6107a58261090a565b60005b8381101561095c578181015183820152602001610944565b50506000910152565b6020815260008251806020840152610984816040850160208701610941565b601f01601f19169190910160400192915050565b600080604083850312156109ab57600080fd5b6109b48361090a565b946020939093013593505050565b6000806000606084860312156109d757600080fd5b6109e08461090a565b92506109ee6020850161090a565b9150604084013590509250925092565b60008060408385031215610a1157600080fd5b610a1a8361090a565b9150610a286020840161090a565b90509250929050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561056257610562610a31565b600082610a7757634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761056257610562610a31565b600060208284031215610aa557600080fd5b815180151581146107a557600080fd5b60008251610ac7818460208701610941565b919091019291505056fea2646970667358221220aa9a251bde32306273cb5f6045040ac4b74b767bd02205c60c6003c5346ac34c64736f6c63430008140033", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000007b74591c97f086c1057bee", + "0x4f2aab765280a617b8913308bffbaed810827576241edbcd290b48d2b699bf92": "0x0000000000000000000000000000000000000000000580926bcba6406ba40000", + "0xd057d56b4d1539d5c08615edc01a9792908fefc021b63dbdc5db20bf522e882e": "0x00000000000000000000000000000000000000000003920c271ee5a29be97bee" + } + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json index 261150145fc0..ed598dfa1dc5 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json @@ -67,6 +67,10 @@ "0x0000000000000000000000000000000000000000": { "balance": "0x272e0528" }, + "0x000000000000000000000000000000000000bbbb": { + "balance": "0x0", + "code": "0x6042604255" + }, "0x703c4b2bd70c169f5717101caee543299fc946c7": { "balance": "0xde0b6b3a7640000", "storage": { diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 8b701b7d593a..1002741b7206 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -60,15 +60,16 @@ type accountMarshaling struct { } type prestateTracer struct { - env *tracing.VMContext - pre stateMap - post stateMap - to common.Address - config prestateTracerConfig - interrupt atomic.Bool // Atomic flag to signal execution interruption - reason error // Textual reason for the interruption - created map[common.Address]bool - deleted map[common.Address]bool + env *tracing.VMContext + pre stateMap + post stateMap + to common.Address + config prestateTracerConfig + chainConfig *params.ChainConfig + interrupt atomic.Bool // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption + created map[common.Address]bool + deleted map[common.Address]bool } type prestateTracerConfig struct { @@ -83,11 +84,12 @@ func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *p return nil, err } t := &prestateTracer{ - pre: stateMap{}, - post: stateMap{}, - config: config, - created: make(map[common.Address]bool), - deleted: make(map[common.Address]bool), + pre: stateMap{}, + post: stateMap{}, + config: config, + chainConfig: chainConfig, + created: make(map[common.Address]bool), + deleted: make(map[common.Address]bool), } return &tracers.Tracer{ Hooks: &tracing.Hooks{ @@ -126,6 +128,13 @@ func (t *prestateTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scop case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE): addr := common.Address(stackData[stackLen-2].Bytes20()) t.lookupAccount(addr) + // Lookup the delegation target + if t.chainConfig.IsPrague(t.env.BlockNumber) { + code := t.env.StateDB.GetCode(addr) + if target, ok := types.ParseDelegation(code); ok { + t.lookupAccount(target) + } + } case op == vm.CREATE: nonce := t.env.StateDB.GetNonce(caller) addr := crypto.CreateAddress(caller, nonce) @@ -154,6 +163,13 @@ func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction t.created[t.to] = true } else { t.to = *tx.To() + // Lookup the delegation target + if t.chainConfig.IsPrague(t.env.BlockNumber) { + code := t.env.StateDB.GetCode(t.to) + if target, ok := types.ParseDelegation(code); ok { + t.lookupAccount(target) + } + } } t.lookupAccount(from)