From b5948eeb384a8e3b5999ea64134a29ae3f8f3a4e Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Wed, 2 Jul 2025 21:18:54 +0900 Subject: [PATCH] core/types: add optional bal construction in statedb when triggered by a flag --- cmd/evm/blockrunner.go | 2 +- cmd/geth/main.go | 5 + cmd/utils/flags.go | 12 ++ core/block_validator_test.go | 2 +- core/blockchain.go | 33 ++++-- core/blockchain_test.go | 2 +- core/genesis.go | 2 +- core/state/state_object.go | 11 ++ core/state/statedb.go | 50 +++++++++ core/state/statedb_hooked.go | 10 ++ core/state_processor.go | 17 +++ core/types/bal/bal.go | 106 ++++++++++++------ core/types/bal/bal_encoding.go | 46 ++++---- core/types/bal/bal_encoding_rlp_generated.go | 11 +- core/types/bal/bal_test.go | 11 +- core/types/block.go | 45 +++++++- core/types/gen_header_json.go | 6 + ...2_block_access_list_with_reads_eip7928.ssz | Bin 0 -> 36280 bytes core/vm/evm.go | 5 +- core/vm/instructions_test.go | 2 +- core/vm/interface.go | 4 + core/vm/interpreter.go | 1 + core/vm/jump_table_export.go | 2 + core/vm/runtime/runtime_test.go | 2 +- eth/api_debug.go | 29 +++++ eth/backend.go | 5 + eth/catalyst/api.go | 2 +- eth/ethconfig/config.go | 2 + eth/tracers/js/bigint.go | 2 +- internal/web3ext/web3ext.go | 5 + params/config.go | 31 +++-- tests/block_test.go | 2 +- tests/block_test_util.go | 3 +- 33 files changed, 363 insertions(+), 105 deletions(-) create mode 100644 core/types/testdata/22615532_block_access_list_with_reads_eip7928.ssz diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 31d1ba5ba1..c0191d672a 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -89,7 +89,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) { continue } result := &testResult{Name: name, Pass: true} - if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) { + if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), false, tracer, func(res error, chain *core.BlockChain) { if ctx.Bool(DumpFlag.Name) { if s, _ := chain.State(); s != nil { result.State = dump(s) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2da5c43216..cc03321e8f 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -151,6 +151,7 @@ var ( utils.BeaconGenesisTimeFlag, utils.BeaconCheckpointFlag, utils.BeaconCheckpointFileFlag, + utils.BuildBALFlag, }, utils.NetworkFlags, utils.DatabaseFlags) rpcFlags = []cli.Flag{ @@ -338,6 +339,10 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { log.Warn(`The "unlock" flag has been deprecated and has no effect`) } + if ctx.IsSet(utils.BuildBALFlag.Name) { + log.Warn(`block-access-list construction enabled. This is an experimental feature that shouldn't be enabled outside of a Geth development context.'`) + } + // Register wallet event handlers to open and auto-derive wallets events := make(chan accounts.WalletEvent, 16) stack.AccountManager().Subscribe(events) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d3e842149a..3f5c263722 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -966,6 +966,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } + + // Block Access List flags + BuildBALFlag = &cli.BoolFlag{ + Name: "buildbal", + Usage: "Enable block-access-list building when executing blocks (experimental)", + Category: flags.MiscCategory, + } ) var ( @@ -1852,6 +1859,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name) } } + + if ctx.IsSet(BuildBALFlag.Name) { + enabled := ctx.Bool(BuildBALFlag.Name) + cfg.BuildBAL = &enabled + } } // MakeBeaconLightConfig constructs a beacon light client config based on the diff --git a/core/block_validator_test.go b/core/block_validator_test.go index fcc99effd0..bbfaaab943 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -210,7 +210,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { t.Fatalf("post-block %d: unexpected result returned: %v", i, result) case <-time.After(25 * time.Millisecond): } - chain.InsertBlockWithoutSetHead(postBlocks[i], false) + chain.InsertBlockWithoutSetHead(postBlocks[i], false, false) } // Verify the blocks with pre-merge blocks and post-merge blocks diff --git a/core/blockchain.go b/core/blockchain.go index 2290b6d3cd..cfef46ecc6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1709,7 +1709,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { } defer bc.chainmu.Unlock() - _, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large) + _, n, err := bc.insertChain(chain, true, false, true) // No witness collection for mass inserts (would get super large) return n, err } @@ -1721,7 +1721,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { // racey behaviour. If a sidechain import is in progress, and the historic state // is imported, but then new canon-head is added before the actual sidechain // completes, then the historic state could be pruned again -func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) { +func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool, makeBAL bool) (*stateless.Witness, int, error) { // If the chain is terminating, don't even bother starting up. if bc.insertStopped() { return nil, 0, nil @@ -1879,7 +1879,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness } // The traced section of block import. start := time.Now() - res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) + res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, makeBAL) if err != nil { return nil, it.index, err } @@ -1947,7 +1947,7 @@ type blockProcessingResult struct { // processBlock executes and validates the given block. If there was no error // it writes the block and associated state to database. -func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) { +func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, makeBAL bool) (_ *blockProcessingResult, blockEndErr error) { var ( err error startTime = time.Now() @@ -2006,6 +2006,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s }(time.Now(), throwaway, block) } + constructBAL := bc.chainConfig.IsByzantium(block.Number()) && makeBAL && bc.cfg.VmConfig.BALConstruction + if constructBAL { + statedb.EnableBALConstruction() + } + // If we are past Byzantium, enable prefetching to pull in trie node paths // while processing transactions. Before Byzantium the prefetcher is mostly // useless due to the intermediate root hashing after each transaction. @@ -2053,6 +2058,16 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s } vtime := time.Since(vstart) + if constructBAL { + // very ugly... deep-copy the block body before setting the block access + // list on it to prevent mutating the block instance passed by the caller. + existingBody := block.Body() + block = block.WithBody(*existingBody) + existingBody = block.Body() + existingBody.AccessList = statedb.BlockAccessList().ToEncodingObj() + block = block.WithBody(*existingBody) + } + // If witnesses was generated and stateless self-validation requested, do // that now. Self validation should *never* run in production, it's more of // a tight integration to enable running *all* consensus tests through the @@ -2226,7 +2241,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma // memory here. if len(blocks) >= 2048 || memory > 64*1024*1024 { log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64()) - if _, _, err := bc.insertChain(blocks, true, false); err != nil { + if _, _, err := bc.insertChain(blocks, true, false, false); err != nil { return nil, 0, err } blocks, memory = blocks[:0], 0 @@ -2240,7 +2255,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma } if len(blocks) > 0 { log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64()) - return bc.insertChain(blocks, true, makeWitness) + return bc.insertChain(blocks, true, makeWitness, false) } return nil, 0, nil } @@ -2289,7 +2304,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co } else { b = bc.GetBlock(hashes[i], numbers[i]) } - if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil { + if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0, false); err != nil { return b.ParentHash(), err } } @@ -2509,13 +2524,13 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error // The key difference between the InsertChain is it won't do the canonical chain // updating. It relies on the additional SetCanonical call to finalize the entire // procedure. -func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) { +func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool, makeBAL bool) (*stateless.Witness, error) { if !bc.chainmu.TryLock() { return nil, errChainStopped } defer bc.chainmu.Unlock() - witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness) + witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness, makeBAL) return witness, err } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 5e768fccdf..ef9f57d4cb 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3456,7 +3456,7 @@ func testSetCanonical(t *testing.T, scheme string) { gen.AddTx(tx) }) for _, block := range side { - _, err := chain.InsertBlockWithoutSetHead(block, false) + _, err := chain.InsertBlockWithoutSetHead(block, false, false) if err != nil { t.Fatalf("Failed to insert into chain: %v", err) } diff --git a/core/genesis.go b/core/genesis.go index f1a620da57..e519fd4270 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -681,7 +681,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { }, } if faucet != nil { - genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))} + genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Lsh(big.NewInt(1), 95)} } return genesis } diff --git a/core/state/state_object.go b/core/state/state_object.go index 767f469bfd..f45e6298e2 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -51,6 +51,10 @@ type stateObject struct { origin *types.StateAccount // Account original data without any change applied, nil means it was not existent data types.StateAccount // Account data with all mutations applied in the scope of block + // TODO: figure out whether or not this is first set after system contracts (withdrawals) are applied + txPreBalance *uint256.Int // the account balance at the start of the current transaction + txPreNonce uint64 + // Write caches. trie Trie // storage trie, which becomes non-nil on first access code []byte // contract bytecode, which gets set when code is loaded @@ -102,6 +106,8 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s addrHash: crypto.Keccak256Hash(address[:]), origin: origin, data: *acct, + txPreBalance: acct.Balance.Clone(), + txPreNonce: acct.Nonce, originStorage: make(Storage), dirtyStorage: make(Storage), pendingStorage: make(Storage), @@ -272,6 +278,9 @@ func (s *stateObject) finalise() { // of the newly-created object as it's no longer eligible for self-destruct // by EIP-6780. For non-newly-created objects, it's a no-op. s.newContract = false + + s.txPreBalance = s.data.Balance.Clone() + s.txPreNonce = s.data.Nonce } // updateTrie is responsible for persisting cached storage changes into the @@ -493,6 +502,8 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject { dirtyCode: s.dirtyCode, selfDestructed: s.selfDestructed, newContract: s.newContract, + txPreNonce: s.txPreNonce, + txPreBalance: s.txPreBalance.Clone(), } if s.trie != nil { obj.trie = mustCopyTrie(s.trie) diff --git a/core/state/statedb.go b/core/state/statedb.go index e805885079..33a3b358e0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -26,6 +26,8 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" @@ -138,6 +140,9 @@ type StateDB struct { // State witness if cross validation is needed witness *stateless.Witness + // block access list, if bal construction is specified + b *bal.ConstructionBlockAccessList + // Measurements gathered during execution for debugging purposes AccountReads time.Duration AccountHashes time.Duration @@ -157,6 +162,19 @@ type StateDB struct { StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition } +// EnableBALConstruction configures the StateDB instance to construct block +// access lists from state reads/writes recorded during block execution +func (s *StateDB) EnableBALConstruction() { + bal := bal.NewConstructionBlockAccessList() + s.b = &bal +} + +// BlockAccessList retrieves the access list that has been constructed +// by the StateDB instance, or nil if BAL construction was not enabled. +func (s *StateDB) BlockAccessList() *bal.ConstructionBlockAccessList { + return s.b +} + // New creates a new state from a given trie. func New(root common.Hash, db Database) (*StateDB, error) { reader, err := db.Reader(root) @@ -373,6 +391,9 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { // GetState retrieves the value associated with the specific key. func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) + if s.b != nil { + s.b.StorageRead(addr, hash) + } if stateObject != nil { return stateObject.GetState(hash) } @@ -629,6 +650,12 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { if obj == nil { obj = s.createObject(addr) } + + if s.b != nil { + // note: when sending a transfer with no value, the target account is loaded here + // we probably want to specify whether this is proper behavior in the EIP + s.b.AccountRead(addr) + } return obj } @@ -765,6 +792,29 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { s.stateObjectsDestruct[obj.address] = obj } } else { + if s.b != nil { + // add written storage keys/values + for key, val := range obj.dirtyStorage { + s.b.StorageWrite(uint16(s.txIndex), obj.address, key, val) + } + + // for addresses that changed balance, add the post-change value + if obj.Balance().Cmp(obj.txPreBalance) != 0 { + s.b.BalanceChange(uint16(s.txIndex), obj.address, obj.Balance()) + } + + // include nonces for any contract-like accounts which incremented them + if common.BytesToHash(obj.CodeHash()) != types.EmptyCodeHash && obj.Nonce() != obj.txPreNonce { + s.b.NonceChange(obj.address, uint16(s.txIndex), obj.Nonce()) + } + + // include code of created contracts + // TODO: validate that this doesn't trigger on delegated EOAs + if obj.newContract { + s.b.CodeChange(obj.address, uint16(s.txIndex), obj.code) + } + } + obj.finalise() s.markUpdate(addr) } diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 3d1ef15031..e49f173ad4 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -19,6 +19,8 @@ package state import ( "math/big" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" @@ -157,6 +159,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness { return s.inner.Witness() } +func (s *hookedStateDB) BlockAccessList() *bal.ConstructionBlockAccessList { + return s.inner.BlockAccessList() +} + func (s *hookedStateDB) AccessEvents() *AccessEvents { return s.inner.AccessEvents() } @@ -261,6 +267,10 @@ func (s *hookedStateDB) AddLog(log *types.Log) { } } +func (s *hookedStateDB) TxIndex() int { + return s.inner.TxIndex() +} + func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { defer s.inner.Finalise(deleteEmptyObjects) if s.hooks.OnBalanceChange == nil { diff --git a/core/state_processor.go b/core/state_processor.go index ee98326467..8a91dfc015 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -82,12 +82,23 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg context = NewEVMBlockContext(header, p.chain, nil) evm := vm.NewEVM(context, tracingStateDB, p.config, cfg) + // process beacon-root and parent block system contracts. + // do not include the storage writes in the BAL: + // * beacon root will be provided as a standalone field in the BAL + // * parent block hash is already in the header field of the block + if statedb.BlockAccessList() != nil { + statedb.BlockAccessList().DisableMutations() + } if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, evm) } + // TODO: set the beacon root on the BAL if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) { ProcessParentBlockHash(block.ParentHash(), evm) } + if statedb.BlockAccessList() != nil { + statedb.BlockAccessList().EnableMutations() + } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { @@ -104,6 +115,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } + + // don't write post-block state mutations to the BAL to save on size. + // these can be easily computed in BAL verification. + if statedb.BlockAccessList() != nil { + statedb.BlockAccessList().DisableMutations() + } // Read requests if Prague is enabled. var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { diff --git a/core/types/bal/bal.go b/core/types/bal/bal.go index fca54f7681..d1b8579a40 100644 --- a/core/types/bal/bal.go +++ b/core/types/bal/bal.go @@ -38,26 +38,26 @@ type ConstructionAccountAccess struct { // StorageWrites is the post-state values of an account's storage slots // that were modified in a block, keyed by the slot key and the tx index // where the modification occurred. - StorageWrites map[common.Hash]map[uint16]common.Hash `json:"storageWrites,omitempty"` + StorageWrites map[common.Hash]map[uint16]common.Hash // StorageReads is the set of slot keys that were accessed during block // execution. // // Storage slots which are both read and written (with changed values) // appear only in StorageWrites. - StorageReads map[common.Hash]struct{} `json:"storageReads,omitempty"` + StorageReads map[common.Hash]struct{} // BalanceChanges contains the post-transaction balances of an account, // keyed by transaction indices where it was changed. - BalanceChanges map[uint16]*uint256.Int `json:"balanceChanges,omitempty"` + BalanceChanges map[uint16]*uint256.Int // NonceChanges contains the post-state nonce values of an account keyed // by tx index. - NonceChanges map[uint16]uint64 `json:"nonceChanges,omitempty"` + NonceChanges map[uint16]uint64 // CodeChange is only set for contract accounts which were deployed in // the block. - CodeChange *CodeChange `json:"codeChange,omitempty"` + CodeChange *CodeChange } // NewConstructionAccountAccess initializes the account access object. @@ -74,6 +74,7 @@ func NewConstructionAccountAccess() *ConstructionAccountAccess { // in execution (account addresses and storage keys). type ConstructionBlockAccessList struct { Accounts map[common.Address]*ConstructionAccountAccess + enabled bool } // NewConstructionBlockAccessList instantiates an empty access list. @@ -83,44 +84,69 @@ func NewConstructionBlockAccessList() ConstructionBlockAccessList { } } +// EnableMutations enables the modification of the access list via subsequent +// calls to record access events. BALs are mutable by default. +func (c *ConstructionBlockAccessList) EnableMutations() { + c.enabled = true +} + +// DisableMutations prevents the mutation of the access list by subsequent calls +// to record access events. +func (c *ConstructionBlockAccessList) DisableMutations() { + c.enabled = false +} + // AccountRead records the address of an account that has been read during execution. -func (b *ConstructionBlockAccessList) AccountRead(addr common.Address) { - if _, ok := b.Accounts[addr]; !ok { - b.Accounts[addr] = NewConstructionAccountAccess() +func (c *ConstructionBlockAccessList) AccountRead(addr common.Address) { + if !c.enabled { + return + } + + if _, ok := c.Accounts[addr]; !ok { + c.Accounts[addr] = NewConstructionAccountAccess() } } // StorageRead records a storage key read during execution. -func (b *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) { - if _, ok := b.Accounts[address]; !ok { - b.Accounts[address] = NewConstructionAccountAccess() - } - if _, ok := b.Accounts[address].StorageWrites[key]; ok { +func (c *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) { + if !c.enabled { return } - b.Accounts[address].StorageReads[key] = struct{}{} + if _, ok := c.Accounts[address]; !ok { + c.Accounts[address] = NewConstructionAccountAccess() + } + if _, ok := c.Accounts[address].StorageWrites[key]; ok { + return + } + c.Accounts[address].StorageReads[key] = struct{}{} } // StorageWrite records the post-transaction value of a mutated storage slot. // The storage slot is removed from the list of read slots. -func (b *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) { - if _, ok := b.Accounts[address]; !ok { - b.Accounts[address] = NewConstructionAccountAccess() +func (c *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) { + if !c.enabled { + return } - if _, ok := b.Accounts[address].StorageWrites[key]; !ok { - b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash) + if _, ok := c.Accounts[address]; !ok { + c.Accounts[address] = NewConstructionAccountAccess() } - b.Accounts[address].StorageWrites[key][txIdx] = value + if _, ok := c.Accounts[address].StorageWrites[key]; !ok { + c.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash) + } + c.Accounts[address].StorageWrites[key][txIdx] = value - delete(b.Accounts[address].StorageReads, key) + delete(c.Accounts[address].StorageReads, key) } // CodeChange records the code of a newly-created contract. -func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) { - if _, ok := b.Accounts[address]; !ok { - b.Accounts[address] = NewConstructionAccountAccess() +func (c *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) { + if !c.enabled { + return } - b.Accounts[address].CodeChange = &CodeChange{ + if _, ok := c.Accounts[address]; !ok { + c.Accounts[address] = NewConstructionAccountAccess() + } + c.Accounts[address].CodeChange = &CodeChange{ TxIndex: txIndex, Code: bytes.Clone(code), } @@ -128,32 +154,38 @@ func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex // NonceChange records tx post-state nonce of any contract-like accounts whose // nonce was incremented. -func (b *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) { - if _, ok := b.Accounts[address]; !ok { - b.Accounts[address] = NewConstructionAccountAccess() +func (c *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) { + if !c.enabled { + return } - b.Accounts[address].NonceChanges[txIdx] = postNonce + if _, ok := c.Accounts[address]; !ok { + c.Accounts[address] = NewConstructionAccountAccess() + } + c.Accounts[address].NonceChanges[txIdx] = postNonce } // BalanceChange records the post-transaction balance of an account whose // balance changed. -func (b *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) { - if _, ok := b.Accounts[address]; !ok { - b.Accounts[address] = NewConstructionAccountAccess() +func (c *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) { + if !c.enabled { + return } - b.Accounts[address].BalanceChanges[txIdx] = balance.Clone() + if _, ok := c.Accounts[address]; !ok { + c.Accounts[address] = NewConstructionAccountAccess() + } + c.Accounts[address].BalanceChanges[txIdx] = balance.Clone() } // PrettyPrint returns a human-readable representation of the access list -func (b *ConstructionBlockAccessList) PrettyPrint() string { - enc := b.toEncodingObj() +func (c *ConstructionBlockAccessList) PrettyPrint() string { + enc := c.ToEncodingObj() return enc.PrettyPrint() } // Copy returns a deep copy of the access list. -func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList { +func (c *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList { res := NewConstructionBlockAccessList() - for addr, aa := range b.Accounts { + for addr, aa := range c.Accounts { var aaCopy ConstructionAccountAccess slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites)) diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index d7d08801b1..29e518d436 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -40,7 +40,7 @@ import ( // BlockAccessList is the encoding format of ConstructionBlockAccessList. type BlockAccessList struct { - Accesses []AccountAccess `ssz-max:"300000"` + Accesses []AccountAccess `ssz-max:"300000" json:"accesses"` } // Validate returns an error if the contents of the access list are not ordered @@ -86,26 +86,26 @@ func encodeBalance(val *uint256.Int) [16]byte { // encodingBalanceChange is the encoding format of BalanceChange. type encodingBalanceChange struct { - TxIdx uint16 `ssz-size:"2"` - Balance [16]byte `ssz-size:"16"` + TxIdx uint16 `ssz-size:"2" json:"txIndex"` + Balance [16]byte `ssz-size:"16" json:"balance"` } // encodingAccountNonce is the encoding format of NonceChange. type encodingAccountNonce struct { - TxIdx uint16 `ssz-size:"2"` - Nonce uint64 `ssz-size:"8"` + TxIdx uint16 `ssz-size:"2" json:"txIndex"` + Nonce uint64 `ssz-size:"8" json:"nonce"` } // encodingStorageWrite is the encoding format of StorageWrites. type encodingStorageWrite struct { - TxIdx uint16 - ValueAfter [32]byte `ssz-size:"32"` + TxIdx uint16 `json:"txIndex"` + ValueAfter common.Hash `ssz-size:"32" json:"valueAfter"` } // encodingStorageWrite is the encoding format of SlotWrites. type encodingSlotWrites struct { - Slot [32]byte `ssz-size:"32"` - Accesses []encodingStorageWrite `ssz-max:"300000"` + Slot common.Hash `ssz-size:"32" json:"slot"` + Accesses []encodingStorageWrite `ssz-max:"300000" json:"accesses"` } // validate returns an instance of the encoding-representation slot writes in @@ -121,12 +121,12 @@ func (e *encodingSlotWrites) validate() error { // AccountAccess is the encoding format of ConstructionAccountAccess. type AccountAccess struct { - Address [20]byte `ssz-size:"20"` // 20-byte Ethereum address - StorageWrites []encodingSlotWrites `ssz-max:"300000"` // Storage changes (slot -> [tx_index -> new_value]) - StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys - BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance]) - NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce]) - Code []CodeChange `ssz-max:"1"` // Code changes ([tx_index -> new_code]) + Address common.Address `ssz-size:"20" json:"address,omitempty"` // 20-byte Ethereum address + StorageWrites []encodingSlotWrites `ssz-max:"300000" json:"storageWrites,omitempty"` // Storage changes (slot -> [tx_index -> new_value]) + StorageReads []common.Hash `ssz-max:"300000" json:"storageReads,omitempty"` // Read-only storage keys + BalanceChanges []encodingBalanceChange `ssz-max:"300000" json:"balanceChanges,omitempty"` // Balance changes ([tx_index -> post_balance]) + NonceChanges []encodingAccountNonce `ssz-max:"300000" json:"nonceChanges,omitempty"` // Nonce changes ([tx_index -> new_nonce]) + Code []CodeChange `ssz-max:"1" json:"code,omitempty"` // Code changes ([tx_index -> new_code]) } // validate converts the account accesses out of encoding format. @@ -146,7 +146,7 @@ func (e *AccountAccess) validate() error { } // Check the storage read slots are sorted in order - if !slices.IsSortedFunc(e.StorageReads, func(a, b [32]byte) int { + if !slices.IsSortedFunc(e.StorageReads, func(a, b common.Hash) int { return bytes.Compare(a[:], b[:]) }) { return errors.New("storage read slots not in lexicographic order") @@ -201,8 +201,8 @@ func (e *AccountAccess) Copy() AccountAccess { } // EncodeRLP returns the RLP-encoded access list -func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error { - return b.toEncodingObj().EncodeRLP(wr) +func (c *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error { + return c.ToEncodingObj().EncodeRLP(wr) } var _ rlp.Encoder = &ConstructionBlockAccessList{} @@ -213,7 +213,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc res := AccountAccess{ Address: addr, StorageWrites: make([]encodingSlotWrites, 0), - StorageReads: make([][32]byte, 0), + StorageReads: make([]common.Hash, 0), BalanceChanges: make([]encodingBalanceChange, 0), NonceChanges: make([]encodingAccountNonce, 0), Code: nil, @@ -279,18 +279,18 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc return res } -// toEncodingObj returns an instance of the access list expressed as the type +// ToEncodingObj returns an instance of the access list expressed as the type // which is used as input for the encoding/decoding. -func (b *ConstructionBlockAccessList) toEncodingObj() *BlockAccessList { +func (c *ConstructionBlockAccessList) ToEncodingObj() *BlockAccessList { var addresses []common.Address - for addr := range b.Accounts { + for addr := range c.Accounts { addresses = append(addresses, addr) } slices.SortFunc(addresses, common.Address.Cmp) var res BlockAccessList for _, addr := range addresses { - res.Accesses = append(res.Accesses, b.Accounts[addr].toEncodingObj(addr)) + res.Accesses = append(res.Accesses, c.Accounts[addr].toEncodingObj(addr)) } return &res } diff --git a/core/types/bal/bal_encoding_rlp_generated.go b/core/types/bal/bal_encoding_rlp_generated.go index 0d52395329..af823a7502 100644 --- a/core/types/bal/bal_encoding_rlp_generated.go +++ b/core/types/bal/bal_encoding_rlp_generated.go @@ -2,6 +2,7 @@ package bal +import "github.com/ethereum/go-ethereum/common" import "github.com/ethereum/go-ethereum/rlp" import "io" @@ -81,7 +82,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { return err } // Address: - var _tmp3 [20]byte + var _tmp3 common.Address if err := dec.ReadBytes(_tmp3[:]); err != nil { return err } @@ -98,7 +99,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { return err } // Slot: - var _tmp6 [32]byte + var _tmp6 common.Hash if err := dec.ReadBytes(_tmp6[:]); err != nil { return err } @@ -121,7 +122,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { } _tmp8.TxIdx = _tmp9 // ValueAfter: - var _tmp10 [32]byte + var _tmp10 common.Hash if err := dec.ReadBytes(_tmp10[:]); err != nil { return err } @@ -147,12 +148,12 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { } _tmp2.StorageWrites = _tmp4 // StorageReads: - var _tmp11 [][32]byte + var _tmp11 []common.Hash if _, err := dec.List(); err != nil { return err } for dec.MoreDataInList() { - var _tmp12 [32]byte + var _tmp12 common.Hash if err := dec.ReadBytes(_tmp12[:]); err != nil { return err } diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go index 29414e414e..5dfa5db7e9 100644 --- a/core/types/bal/bal_test.go +++ b/core/types/bal/bal_test.go @@ -87,6 +87,7 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList { }, }, }, + true, } } @@ -102,10 +103,10 @@ func TestBALEncoding(t *testing.T) { if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 10000000)); err != nil { t.Fatalf("decoding failed: %v\n", err) } - if dec.Hash() != bal.toEncodingObj().Hash() { + if dec.Hash() != bal.ToEncodingObj().Hash() { t.Fatalf("encoded block hash doesn't match decoded") } - if !equalBALs(bal.toEncodingObj(), &dec) { + if !equalBALs(bal.ToEncodingObj(), &dec) { t.Fatal("decoded BAL doesn't match") } } @@ -113,7 +114,7 @@ func TestBALEncoding(t *testing.T) { func makeTestAccountAccess(sort bool) AccountAccess { var ( storageWrites []encodingSlotWrites - storageReads [][32]byte + storageReads []common.Hash balances []encodingBalanceChange nonces []encodingAccountNonce ) @@ -144,7 +145,7 @@ func makeTestAccountAccess(sort bool) AccountAccess { storageReads = append(storageReads, testrand.Hash()) } if sort { - slices.SortFunc(storageReads, func(a, b [32]byte) int { + slices.SortFunc(storageReads, func(a, b common.Hash) int { return bytes.Compare(a[:], b[:]) }) } @@ -245,7 +246,7 @@ func TestBlockAccessListValidation(t *testing.T) { // Validate the derived block access list cBAL := makeTestConstructionBAL() - listB := cBAL.toEncodingObj() + listB := cBAL.ToEncodingObj() if err := listB.Validate(); err != nil { t.Fatalf("Unexpected validation error: %v", err) } diff --git a/core/types/block.go b/core/types/block.go index b284fb3b16..c3baf75935 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -18,6 +18,7 @@ package types import ( + "bytes" "crypto/sha256" "encoding/binary" "fmt" @@ -28,6 +29,8 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" @@ -106,6 +109,8 @@ type Header struct { // RequestsHash was added by EIP-7685 and is ignored in legacy headers. RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + + BALHash *common.Hash `json:"balHash" rlp:"-"` } // field type overrides for gencodec @@ -183,7 +188,8 @@ func (h *Header) EmptyReceipts() bool { type Body struct { Transactions []*Transaction Uncles []*Header - Withdrawals []*Withdrawal `rlp:"optional"` + Withdrawals []*Withdrawal `rlp:"optional"` + AccessList *bal.BlockAccessList `rlp:"optional,nil"` } // Block represents an Ethereum block. @@ -214,6 +220,8 @@ type Block struct { // that process it. witness *ExecutionWitness + accessList *bal.BlockAccessList + // caches hash atomic.Pointer[common.Hash] size atomic.Uint64 @@ -230,6 +238,7 @@ type extblock struct { Txs []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` + BAL []byte `rlp:"optional"` } // NewBlock creates a new block. The input data is copied, changes to header and to the @@ -290,6 +299,12 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.withdrawals = slices.Clone(withdrawals) } + if body.AccessList != nil { + balHash := body.AccessList.Hash() + b.header.BALHash = &balHash + b.accessList = body.AccessList + } + return b } @@ -334,30 +349,50 @@ func CopyHeader(h *Header) *Header { // DecodeRLP decodes a block from RLP. func (b *Block) DecodeRLP(s *rlp.Stream) error { - var eb extblock + var ( + eb extblock + err error + ) _, size, _ := s.Kind() if err := s.Decode(&eb); err != nil { return err } b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals + if eb.BAL != nil { + err = b.accessList.DecodeRLP(rlp.NewStream(bytes.NewBuffer(eb.BAL), 10_000_000)) + if err != nil { + return err + } + } + // TODO: ensure that BAL is accounted for in size b.size.Store(rlp.ListSize(size)) return nil } // EncodeRLP serializes a block as RLP. func (b *Block) EncodeRLP(w io.Writer) error { + var alEnc []byte + var err error + + if b.accessList != nil { + err = b.accessList.EncodeRLP(bytes.NewBuffer(alEnc)) + if err != nil { + return err + } + } return rlp.Encode(w, &extblock{ Header: b.header, Txs: b.transactions, Uncles: b.uncles, Withdrawals: b.withdrawals, + BAL: alEnc, }) } // Body returns the non-header content of the block. // Note the returned data is not an independent copy. func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals} + return &Body{b.transactions, b.uncles, b.withdrawals, b.accessList} } // Accessors for body data. These do not return a copy because the content @@ -508,6 +543,10 @@ func (b *Block) WithBody(body Body) *Block { withdrawals: slices.Clone(body.Withdrawals), witness: b.witness, } + if body.AccessList != nil { + balCopy := body.AccessList.Copy() + block.accessList = &balCopy + } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) } diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 0af12500bd..c9690b792d 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -37,6 +37,7 @@ func (h Header) MarshalJSON() ([]byte, error) { ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + BALHash *common.Hash `json:"balHash" rlp:"-"` Hash common.Hash `json:"hash"` } var enc Header @@ -61,6 +62,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) enc.ParentBeaconRoot = h.ParentBeaconRoot enc.RequestsHash = h.RequestsHash + enc.BALHash = h.BALHash enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -89,6 +91,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + BALHash *common.Hash `json:"balHash" rlp:"-"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -169,5 +172,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.RequestsHash != nil { h.RequestsHash = dec.RequestsHash } + if dec.BALHash != nil { + h.BALHash = dec.BALHash + } return nil } diff --git a/core/types/testdata/22615532_block_access_list_with_reads_eip7928.ssz b/core/types/testdata/22615532_block_access_list_with_reads_eip7928.ssz new file mode 100644 index 0000000000000000000000000000000000000000..a3f23822a81c995a4b4679dca0bb1676d7a479e3 GIT binary patch literal 36280 zcmcG%2Rv8p|37~0J(HE}y)v>>c1DRrvXYTqQpw&SE26S8v$OZ8tPqi1Hkk>Dh!X$n z;Emg@+`q@?`+q$iPv@N1b-kX~p65Dms1O9{pg@o&Dg<3ZgCHq%2#N+629U>qppTdk zM1=)G`T!N!z%vd6i2}F)lmM{fLJ%o21nB`J0I(l{pc??G0COY|#CjBhECG@Mg2*7K z0bq^-f@CNG2LK}#1epT70GI%vJq|%e0JQ+?06f$XbO)f59)gsaAjlV>8UURcf+PSO z0IC7dSs+Le;5jPP608C$lpgKd4-^L&;faL2SUrj(B z0Q3S-n?jHsKm`C~2I>yL9e^x=ICBWXv;gG+;0-`-3F;X@B)}kmsWk-U00`TF@a}-L z1H{~gpicl)wh*KN5Dd@=(0>o)s~yN!0LTNx5x^Iq8UWoB)D-{+fC7LOfKy(eyZ~we zFz*9906&0k0BauzN(UhJ1#tuL2B-wUd;p#Sya5^k`1~Nq7N8S=+#kd-0OSe48h~IR z-~reaRGD+S^*Z^Q!g$=`!+yc6Kv@B>wg;Ht9ux(XWsOGHP;cj}jPuMN!#GwXGkPN1 z&f410FG=8cb25$(Bk%*N0uY=M_%Fd2{1*t4NR{%tXA@a;KQQPr4q{kQ0Z zxU=dLGR}D&G({V$s95Lpd=UM>)=_fPy=+F!I(LRE@d30PMjcBRxb#^g{QeZ#VBlSyPKfAKMXwF?|; zj!jZHxBet=+(tq^hOG9#tBsPs7rS`vBU)dPp1NCPI4j;v5*62Nn1u-d^-lzw`YWlo z3I%d5%%Dm0FlE?Q&-9*-dnz6mrWk-PrD!636zTnRJq7_w%S@7NW3iM( z#9mx#KfU^7{HqQ3Q}QwqVq;7RUO3k500#=nQm8Ev3B~#?RbJbY;y22b4h1)F!|b5jgk%7AEFlu?pl6N=XnB}W zbq&%c8R#)YUp5%ThaA5btbFfxzFq4shbi!Zz_#Z{Y5VzVg|q>$v)Q?}9~xPDT<(e-maBam+@_y*6)^5?=mb>}I0o^50oDqlo-u ze5jA(L`G|#=y~iRdTfKclqe_E(agq3Z%j961Vwb)ns2de{7Kdea7HBq+77FSg#?kG zHR#6drag(GZj*<*(VkMpihXtcuAAa}3hD}9BUCo-Z)6IO*AlgrBuWg(!(H11zQE;y z0HW^5?&Pd^l*{XlDBDO`Xzuuy5^Y1L{EJ9lJ88Q0C@a3E?@fG*CWJkFMQ}*$a6s6| zMQ^ljlS}OMFzBU5JvVjVyJS6cQjfx#ZDBmG6a?GCV}K5*7YJa2XSgr8<)knF$`{Ah zfrT@eS%H7)Yyvs;xuW_S-O<~Zuj~9e1?S$@7yxee302T&2{O&;yNd3g)4ALm`IU+( zO-NJd{hLNr+dWIUpPqFZ%D+nYW#TQ(-&l|%Ieta^#L*Zu9aaX?=*$Y=?sU6qC}h9x z60yT&XwpeDfNTh|u13n;c_yTNbtd;%pV1H3ksmR#eK)VBnBmyfUvV`pe=;n9!`gVJ z->^oof%(dDGlPaxB7Re>XDxqbQr>&?J8i1#xnF72{jQ;p^ucTrb!S)@W7JN$P1;V4=l zWwgCzY(&g0vHg}Zz&F~irQ}V5pFc|n(V#`-EhYYY-PJakr)+d&NFlfIHA{Zo40hko zu|ITL)Z9hR00OQvzL(XzPVf(0P80jABk0yE+zDHP>C!)d3Z7r4YhJ?=en##5cr7f;h!BlqAU4-x=EH(y>G=e{`{<6% zI(y?f0}={jS1{6^(oo#i>q7Lz5QLGIF6{R^t7|*?z$-=uf1Cffdo{|A8O96jD~awQ z)o{$W1mnfRob-%wn_l(%kB;GK76$WCebkRyP6#G zrLoEjomhJ`{?m#gM>?_hXx7K%PS$ts)ENjPmx+u`!K4#+*H5n9vW5jmC*B@SESrdH zQz!l&%^Mx6FpExtT}`3ck(Z~3I^hvuOE3x_%EhUpqq*Jq(NaTzYLDhY z8?l?5z;-tZ^ccmlAI^md3V_AjwqO*OIYl|tG}9myz)x*ko_Wy#?H>dqq;T2PrLHRuzGJ~1n zcY3>H2}U`G)!eBXJ;gnKI3=AWMT3;~YSc8`v*yn2)fk_sW>7x=4^7>Z%hT`-VoNZ} zIj>i&=+cAb{T|Jma+6Og2n7WDX`{a@3a0RT{Pc{w{v;wIBECe|XjOZ${Q<9(Til8l zyD_4#htq=q!tM`O6r%NKCMLt8jWe0|T19WZmbpT%9gpCFuW-G9^LaN0VAo+G`>n0K zDun2ThP&;l)tH*h2Zc7wg}*-brfG*NMvw$?Jz>9bmHtQqN%2A zwM^qIA7k$%q9zZrxFheBFJ{V3%yUpu%-k8PE<54{q4(12xSuKSL;uj%aXI`+bF%!n z6~!CAdv5$*j{-d^#S2bEkTs&DPzkVqJi?NU8&SxV<{g(MTfl#1xHX}eN(Bm3U4bPMa3meM}FSV0rTwE5U}#QJ*%|(lrp4< z+V}iGXk5Byvl-^|3XF-x&G{@k7+mG{03M|-0)zv|X>H8jaLKU6PhLHW5lKy;U_Cr* z*D?91Wj-J$iMpQTdnHj? z%&m!KGiA<}f3pyIvE&!TX*Z-kAN0E7O>}?lqrB@J8eU-D+s=lPn?zRUUd@#iy*`u6 z@+1Z24qwsI{2OP=f3y5EG7$1;?jtvueBy@|$ixZxS&BoBRPmSJ>vFtdJt0K0LEN1x zi++RkcFB)0=?ShV$qTb}uT55zEYJ4yNtmS=;dixo87iNzJZ3p2AYn&P5XCaCIyPz9 z)b5p6e^CDK&QFj@rcMTcMnt2K_>)6h``S?8n0H3eV(AdNmG^#2xF+xRVpp%t+6(<$ zICBarciBn{J;oCaPnj=jkK)fvHYM_R-CXlA+|RMU^ta}k^x>W7e#f=;E;jb{G{)cX#^@pLV~4np zAL2f7i2LLr?o)@je?G*0n!&cMtxt=OaKT0ewYjae`&vW}FQS~`D_lEZAo@mdtLQj8 z8Pf?dj@DhSc3yKyUR?_E7IyZs5KF%TTfqGTGpNo8?Cl?b^WOvqZj+RoKFC6GG+Psp z#u%)?BN&?C{YS1c&B%>SN}LqpBsl$+qA~2=FI8mD%=rss4_B@p zSFT`M^m$(zvVLSRQqC#;frB+ljO6+GjH2(fI{7&U53rn6KYbF+&Lc7XJkT7t$}8(k zg?ZH9?FM?yul_Ulo`h2xPC1&4-VR`uw(+JGcx`%9H}P7O&XtfRBc?C!y7Rf)Z;5;; zWhbq(7d+A{4ZX-uoU)?2x>kx|fW~XA>(urz@mbX=Co_#69ZB{mvy@u;lwMP6M_i?; zi!W|zTMQ0=@-sKRWE*T4~PZ~cPj81U}Y&iooF`8DQrqGt`;&de0)<|%B9wlZ`{ZU`}FVkzkPMfxpV zJK(ogk>b&SJ|Tg0ti7y=TD!vm3Eella;3_NmQ617@$~ZQ725IG`)*S6p8~@Za1gaN z8I&dh|Gm~C8NxMXcRqe#L8DW%1WoZxW%keXRbP$qo!7cSIW*>J5z=OJXMhNQ{rv0? z+%1;7O}6s=3(m(Lo_T#Nx$g~)tDjI{u&}GUb|d`-LppYHx7*9UM2Ej`!J>tztvZe@us~vqkC(}KI^i4 znRRNsF&5|BmrE^*TDFcUL^du9XMB!$OVUkMjXZWg_xzZ9JYUvjRI7*1!Iq5CFV+2jMJcz(GPq9aFh(qj$65CR2$-r}}#qQKz*7rg7boC`M6X&e!9BX-%Mn6s3X7`3-$s~4Q9>lS`CW77$rrZ3; zT;bZ;OXEqMsd+y{T=lxd(zk(ZAZ95g{Ly|6xUTJ;J<;F3%;`qRdo%A_+Hif0vJLM# z@wr?ytRtF&2U+f}Y9ZwJsRB63wR}ztA-*NKq%|GGN#Evv79*ARm+ZHM)4scghX_1x zsf>$cj<0k=ARfZbJp_*F@~&mM}hyQ}_i z9X<0O_Oj~4P;UYX@C!jlpuX)p0p)?NRF=o(?x^WJKDS*)t(_Nd?Bz$aznzsMx|b8$ zcaQVzx&no|5+o#bPK+W6vx?OOzHC3(k){Q-SSWFAQjTds-!eCazlST-l*7xJL6XpoC&5Xwd=Yhyw}JN8JS+ zUt53*0AvsWd4M2*P5?^4r4Nt{Fay8`Y`GY39`(4rP8vC!^6dLj;XW-XF_DE0h4xI8 z&n0Q|)|)#5yX#IsdY~M_Qp5+$XLrJ3|Y97jcH zKcse6{Wq2_s;^?Y_Ja})(m$hx6C2)GbuKSZ&cA4rK$2)2^=moz-&hW87Q4#pySE44 zy*;D_l^iyn74ou}Dr)%i-p)Ewx{{1^gYmRs(bOD~U3UuEeH`6uD1AQqxnieezNx1X zuJtxqQQb|O%p$@UdHGHB`8CaH<8ra{_KMeeE*!x=U$UTn46+`2uag4zFOi_%Z~%P; z0`PdH1rP@?0l*2|4-rf_2NC-&><_kmZ!h`36>U8uu7Bhr-pc~}xu(-GI44rk!+xAI#0K*b!fpXH$NF44)8 zM5N|+7b*T!8)#*xrL~f*^S7w8d6ecK8@5q-_CeMb%kz)PzdI~)A9)wcTX)o`B4beM zaJ2NHd^+a;>eU_Xg9L_cpYG1h0puILvY zbmf;AJnVdg_gl(0)pNhmPCf-;eIoh)2ipw#Dj?-Clv zehxU^yZg%EflSIB1CqoHeou4V&^X+%xy#9l`$U=g!IpdHWLtY0lE+6}iQ5vD-?_?u z50^18FsawudG{p`=n(+7e-mU$gNXD&Bk76dsSFe18^$Ti=yOZ^;|`}Ev1UdlUMg%4 zi@w5YN&#_OZ_QaH=UC^^PExxL^ao0dCF@^m=HWbWpAwGKmf%$mt8K)c?URSbpKRr! z^w9{snCXq7?9K?yg8MYIXX1%(BH{thT(q`I z&^z7Bs?RFtDrKKAk<(b0j$NQMQ=u9w;_NZrksxcs$E@^)A(VF`ap}_S<3?1MGO|RE zQj3@dWF#s|uSvZ3+u`NWBpZz|JSUa#2@wrrf_}Es zGhm0mf*6^1K_hHb%d6fa4;8%?wQwXcb514=X^1YQ<3?vK!;_m~#o3QOlTI(@j-f=K z%0)62yo}4+(_>*;MKYVn|A7894rzO|`!)-h1;M|CY%$@wqpmnfRuo%k(wN@p*}&O0>rpH_C(sLNH4 zjzW&NFgKYMWLvn7Y4@NuHi#7e&d6?y&ImuSmF(`hTaoDXH?W?yQ%V{82Cd$Gx1sIj zuBMr+*`U3Vvp)rJDt6z}`1^i2)gr}haxU__NN%$2v7k%O0_n}={47{eQukZJwR`uy z!=-pb+?MP!xo4pyC3xKo&aEXzun8$Q_qj;DRUo+?eUf3tzHdJV96H;k@2x*+3xnhq zgv#enw$tJ}{W2_w8PEUmUXDX>`pkYyIP~4tjnZ=yy?55vS7Gzy3(Tc_tA3_^CGX12 zH|Far?8FPhh8H^y;@FDoX6OVc$6qRmyWv6zo$?cg;_)Ks`%IECyOf#2OXOq=2E z`vdP;`SeI%%;`>4z4jodT)NifF|LhcL@N&-6|1_-M5JAk(BUjFX_WqXI@wY&<7Y1n z4Ds)N8$9ad_Lkt0HUA@{yf)LmG_k+2$Pc_(Da8%_RCsk?^!7jg|Gs;>Kfry@SjtcB zc*>MMcvhCX9_{!f{o~Bit(|>>RNxtbeRlgF!3^!Ma@gYQ^kJj;g<#kG$jp`#D|cSu z($+UfY4o#P+g~H$_OSa74iTRVPlfo$Nv~e5|DB}&x>bDEva6!WW1G^ znytl^6nr?wX4Upuo&$dM0k8lz1<5Lmvh}*MXynd@Es|BJ+A_NO_&F>Hq19?ufy4^vgPt-c8@vppv(?qpR4=zZ8CRl6ueHRg@;D*59c^zQ6(j8}v7Z zn(xl}y8gi9Ok{8N$5fs6BK3Sl#w2k`gUFBm+S59!1+BZEv(QhwVwwss-8Q87>>7mH z{ObD0BlxueKkb`xXzHYNC{O0Ds^^`q4s+Lc@HZw5X3X>nixq8>#&Ed&dD73oF`cKF zEYw%~F?%pNo8K~=0vmw5JwOHb`nd|FVjVW(JU3LVuRjkkxAb8PloEXG^0l9n__<9m z1ouL_vqbO!9W=Lu^?wo23;$J6HIZZQ=65yDN6vM0-)U^;Jd=B8eSe<;hfq%6#=2VE;yNnYIhpCzFiJl^F1CexN2keQF+Y9TE|B& zK4;o&@7$mfW$gjnHVgn#0p7 ztL3z`8cGP9nnQNnVIRX>{$40&@2r1_{d!}BW4fHElWuvj6EBlhquE&G2it#PIf5^7 z+2<#5D#P$Xu_0CMF|kw+e}1|~cOs&ncbi2M%iT8jZ2lRSgWfS{F<`uOQX-%xjV-Lm z@;sh?j^g!^I#uM>3Fwl78mAc3e~Amy4gJfvT_cyzq}VV`Nu3!rs%LiHI zm*=?SJ*XGcNPmb=yL}+k_*Nvrb~&Ys{v0oF;ADpN^Y^`1jD&KdW&;dGr`PoLtCnx# zrt^nZMW#KQp|JRnlV#C;p4z9m;KWil->ou(09|c5zl3M!v3V0R&kNoBiO*hP8-#mM zjo+OD;K^q$4>&xG5rZ(d`*636EF*6rC%dQaIfE6{jfX^nBJS^HjORKnhEX(sEYd4QvVbChdEG3hvYOxqiHp^~bq?!BvNq zBgu}L>MikGw_~(P3Yp(81h@CVWh@WySJ@go|Ln#1&rYxlro~~iEB!a0SkHM6O>J;r zrjtFFcdY$#Kk5FwkO54)t0eHY0IxWiT>b*2y&bqQF0ne=qt^?!nBpGY%~MltI<2yL z5C`+Ne{i9;lIYP%)nx|nMYiO+R-ltvw)LIy^koJ;JGSxSgK;A=X50A#B6=v~#>n;h#6Ty46l z2_IiSxyN@>3_;hit*> zV7fX~QOIkyU}W+Jjcv+YmkYrIUlHTm-kQjqcUk7h8H;k{2SdGN_tgie#?n*2y?mWp zF^I5i0G0?G818>p9#;S0ivBmjG$ePvH6k+i*)uf|LrBKyj_T7W@$$RDr)&$?rSSwc zgRI^k=eyDn@B7Jk!=_iehrrls={h1E@Dq5=yd0HuZSgIqc9y;;o=>@Pt0| zuv!()aebs(Ka2dG)}8cUdrC0Rws+VFIi31f*f4ZMQnG`Gu8=dN+8a|sydZ9)5Wfx) zIwA+*D~2Wy;gvWX;f_EY+3ast%Tsr6<5nu`3aey#zttGCsgQ11Xg6|DGx|~5O>pP( z_n@Qw1T7;cdVkr=4YKhzi%&%~8%!{IXL;N4R#ze&yKIT%RFqy(r{gM0aA6UBG2?iw zXx{HuSKZ&C3J)YD&R#?_X7PDtD8kOrCaD?gdh$zK${BOjm%W0b?$6HDB};$1Wz8Dy zHio8pc_iDo`?>>Z2~hd)|~C`N(o&E$gR?#x>&>*Sf2k2XXA4_Jwe#YQYzC z0UQuwGuxTPbdWEn1jpgzrAmw5w~A&1^=U8%ym#RZS_1-m_b$#A#+|sr(H@4w)qLJ#ULs*moFhGz=X zIHzv0{_*cW3I6SEr;7aBX;UL|*r;?!DwGa!zi^1V$|3Hmhqzxf>2I(~OV5^w_k`#! zkDDS9X(glGLmT6>FQ|HpCDommG(f8!Kfj{eQQrmnWeU}~qfZ7$3rgdZ{Q{FUsgJ4F zEI;ovzUDlVlpk44&8=4xh-VZU7oGc7-FGkz(*^U9vywp*YBOF~DXpm%h!M_KW(t_<&Q18@seMOcy?oBJG4Ui7-n zRpiF%H(bXrM6ER3&(k6=`$U|)gRqnbzO}Z2F#OYXr-dSC6x4ofU3;0~Mt`$%jswep zaqb1Cc4=x;Jju1ywnaPt3Wx`-vxcxH&F9_^y{}=1{&W?O{&V8mcEC=z_KAksdJon|t zv9zG+-dD6`Ey!@2Q{D>kz`sb|r&`LsKj0pcD3MI6M{|s@?)MC_3^nyoIU$ccX2QCt zfo0cCbQlXXl~Yqqm7#K^?R#-asLXf08a1EDg_aAfA{eJ#yLzcR#?U?$kRQqd$IuYp z=|c3mSC$erq&8|aSL4i*pA;(%sW;S>O(v;TQ}Ih5)_%=-<3Gl`$?gJv01VI*&m_>~mt__2?<<>4dL& zFF4lg^kfT;gw}=%KWPpV>tv>3%eUt4S*ChsHR6GNE#m30Wq;>fBtx@ao6Ze#mwTVH zrrf=%X)e%tWN5N9iMe3>nr$|m=ZDsq2SiDRiGK>%v!hxOe1CkQx?W=xn+R3m-4Q#t z{F6UD9!3kueTkWOu(^=jkM_%tt15zae@%mH+unH_O>Q5Nu8$XqYn4|uN0ZnhnlN@w zNYVWd6N?8~^6afCoA2(tj-9}lxpM7znsVKx%nRS&7f~E+xi`$McX#)_<%BqExhV}? zTf)*EtobM3V`w45^abG{us6s9?u`#C^}|jAcqgwLoucu(cS?r)v3y~wV{ig{93md@ z712JWb_2%=X{zaapCPrgUi6LW=k6{1jvvh7t=Rb?b83uub!S%FUf~vT&?ocqmrMMEB5LmzXr=6@WapZ+3TZsU zFS5vuO{2R>Mx5ZrWo)^UK*N>cUv?x<=Y$U(+mP#dSs^<0NwPj%`3KJ|deix-@zOdS z0`4p}%p1ip$nGzT{WX}nUO}fp&yVkW?5|Y6vKt#uzU%t8b>=VX?DXdHpyUux7**(8 z^u8VWdu3d=*hE|Nyj)FQGRoXC)1^P)z|WR-OsPl|9V@s zKL#|b`~mq*ehvQQVoqlo5?ARc-MI#?aPr6fYi}Fw;}PF+fLmlU_D(Wa+~eSE+?M!< zOFE+?_vggCaEUb#tt`V`qi;DZ{WQkqGtzqgqjK2txNbIn0p}fb)leF=Q}Rm@mZpFn z0j#0qhy85H~3&@Z6lCeLT%StBd?$Y%`LVaPuB&?()gaMxh zRf8onp}DEVvn#NVW`yFFgPmR-@2#{9rd0IvHg;z(m7KhYazUx-JId3K!9Jsh)iBvH zzAM?9q~)AXzSfEJmn?qBoa7jdtV;+`wtjb-&QVeEw<~&Zw@>Ej*%phwkkAoD_JIyv zc09{>C!CpE2dnZ_-H-ngNo_Z{gJJKN8AQunW^WrFlR96E#fp4~WjgQ171`=g#hY^_ z@&086hA||*UoO)=x)>0Uf!fX3+WqLL@hy}nGI=}B&jKz=Nv@GC#GYfGZm9}au;;QO z(gLJQjDpyvTg;7Q^vwDjJaZH$X~Ty9Enx17861UXyF+i$q}_7hLV5|&Yr(Q#kBKCHJIl%uD8mKnX`(`e0Z#GzC? zJWOCM|JwiAA~Vw%N3VeW=#=qfFwu2l93J^^tT7oLZ6vMPHN__0rYX|}H*TT1&VDC8 z6KjaJV;Fw*jFrbkDzRMj{Shh+n>um`A{0si$5KRn~v-GTh4-yHla-RX|C>jnRQ zdH7F{U}X8L@Ez6~536mVBPX)&P|jSVab2G5%dN+&RI}7OD3||{SDRn9hxtk21XOju zZY{!e;&wWz$k|%4vu;lXkw)Nujo}be#7R9@M;vF|f$@-^g&BQkbA!^fi#C{6Zkg#F zEld7x9>(dVd!kUfv1_f2oTB~FJ6o!PAl@G=w6C~?3C?$ggYO|aDLmM5qT(4;twJJ8 z65Y%WN+I5EMu&#*1c6&4Hd2AL3_RoJTt2aV%r~(xlIl z17loKq{*384jkKZw3!q!-^9POpwcs*2oR`nacf^V(ZMsZ^zgX**KbQV%rE|E)g5~7 za-udD-^%G_CC6V`kVu(7u*I&;YaSTI|E@%Asr<rW2bdYnVt4?91&Q`CRpL2Nz$ z!5MrYq25O&na9GNwMwtkewTneQT+YzO<8kY|JSPvcG@2#RM%f*)T{~5F6DSI6^GO0 z6RFsIFfa}YH>i)fW)gYaUL){reu-=vzt?r0Cxr!k6$R3|C-P&&Ge6qjx8_WWxE&aA zB2h&%9>4ZuOXcV-yEYQ1=dCp+W?16->y%H+%_{O(@LrvsxaA>RRdFt-V`bd2$&5p~ zyU!ErTD)vC`OV5fypx?jUY{gc!3*S5VPgA0x%;0Z%-B znvMV_c!t-nPi85QmDYIkVuT_DPKZ8aeJa%S^dZ5lYn3(1Qp1Sh23)JC0S=PYua>+t zT)nTNq3lWSyEL-UtfGdLHL2+_#d5bk-p`(&L({%yvXb>IuKkTgSadFIjwaSt%>1A* z_UQQc+#ht?a*%S5u4B*rLARZU^`;lC)4gBbJY9;`bXtX>L%UjD!h6=#qQw`l9x?YllMj+KWC0=kF`sr8q}+wNhZ1;Z)4u} zDc;!g*4{o3F?R0mi!;gaNaA+=GKniO3#p^?7p!V~rpO(W(;|reW%KRUG%ENlG;rz& z0SS;UZ-93I#Gte_0b&5g0N6nvXAMvUum)fRHi^>#<^co&j}riru2n~N2BiS9^RcA6 zHH-J3-O%JuY91#Jwd?zEz2U|*T#mcv+`z+rK?yx_ij@!lo)^Dah;>=Q8t01%RFwjL zk#B9C+^d);+i!cLont+e1wwHqZxtc`>LMNDBk{yBO7Vzl{ln4W(d+WbNSJ3j?h8Me z_1`}4{HGr=+SrgJ>`?3u6I;%E?}q+~vUh=>I_ZpW*GM^zPOMkhXklO8IDUdF5*!yM z=+7*Zrq-47HJAHXgE{bXZ-uR9y%yu{9bX;8GHdFq{8gSq%YgFm`AL|CWPe`#;~D)~ zbE=_2y*6XjFZPG_r+(tuc`2z>k>nc{^geND@<@7V_I&df*7lr``G@`PgK_fMVze|% z#Zx*15h_{bZ^-R$R#4C)NDA)9*Vp;U^uuxa=NXb++s(U+&IVTpZ;E4y#!!=&$XDfJ zgslu!jacf&(4T$GEFip6Ev^1?^K0(-}lb%LGqnn(&D2}*nbdqkrgG|MrjC@u=euu?_L3Fx@DtL7KFyDXcH&xVh5FF!|fx!VL$X>@1$2H<0BzQKvw z{BH4YEL*gxtr;oRE{}M!?{Q&QNU9eeM-vtlns}tp#P$)b;!SAgp*aU$-q=@A=x3$ka%<|V#Jd!yazcf?o$5md36yP9S*UI4&rGhr}(xB%1wU~h8)^`8VCu6I&U zCb3o4SH_l7%E#wfUf+Ar#(^aCu(kN)?K(XAZU6ZYbpcpWVxeGqa>=l_k>et(HzsG3UlJ5*_myGU>QQ@JK>)Ci${mmnrTVGPq z2T*)1OqZ8*M;d-4wX*rW5ZJN2s}aEKWY3v*gv{!dWe_88Xu!)M`APEu=Z8kn^RFX^ z&HnbM2q|mju>pxz)>Y{r+GiyCiJ7jPajr2*;rqhP@a5-QX)BXubCi&KcIapm_w+_C z1~kY>p#40qvc>X;G3!CCC(?PefzYmnpH(fm4Qu+b+68I1gW^=qf0D)G z$zTewu}$X-POY+vf#b60^xGaja4S4nQJuQS*0A^*k`p|uJl%7jRRynhMu?UEZUviG zq1Uovd8t^Y6~}oIq+9QsE+G|}J?Nj79uFMKyB#f~on`x6*Yks_p9b}~m4oQgWx|;% zgGZkS190`ylr=F1Q_F4+IWFHVG%?#)e{j|Aq~QlH8W**v%y0BoXPyPSX!mE%)_bc? zH_LS6Tc%?REnfA}xsHa)K+7X0;-7Yu)j&BSZaIuyFt1&SF4!Bx0;Q1g)Tt>iicqOg z9@H+ZFcfpn5x1uy$2XKxKE}zXEU9I)u$f8!u(+!5;o*+~BdqWCh7sBc6M=Ri*DX)o z!KJaEL{_IwO{ngPtavIwr!dk?i!AAo5?Sh?A@mD<(dJ0qY+ocf4X?SOWKcQ%*+yi{ zfsD1NJf?11Hi=_ra~4>S^zbi+4lAMb{rWooEiw~dLZ=+>)?ur%N{aXdA53B_wvwwM z&+&;~(sv73@JC*kOei!_80KxJYeKGfvu`o_biEtt))E6-nTf?w+lYHTdRzjgGbRo+ zs?8iTr;Zo6#g{T*Dw>*jhFp-v$9=5kas2x@6_kYK%WWe_?{k7r7M&E? z;Eq`-DUN(`%!@>HxhK3M1gC-tQ}e16c7f1~TP&X6=4|zOMyKZI+*{b(7Ge{6IC*N% zjUsanq1z>-oV!N%s=W;D0ou2hZ44*_zpr=&fA(awA@-kYCW6p?U#v(s-WKPagT^1f z@V-jS&3THSFY#WwDZT!wH{8lG-P3WhwKtaYKOHo-?4H6tWjBYL>Xx!E`q zhjkrC(QL0mN=!^o=KG_RoEnJtbHFPchTW#jkVVn}IN!eHVz zr=&k@5AQ~y13w7t-HkeD8?5s=j^lAx(l@>S0Z!a?XPa9&;m2?><_5T|4GNdxzW8tN zq`@y?{_2JfVfk@53w*ldvIB2Z@sAwBFKuz2W3>;Wn$=29eXdouY2jlin=LOo^!+%T zF{s6dmDnM1T{*;k^^jOXhqxm}-HRfV;=)^{XF&4jD`@*EH+;)R+?4E-ycHxi^X4kF zK6c8+G|`LWl&=nTb3CPI?vKHgd%XjgT6%nzW}yAEBKvJslB`#=F$K9!V9jI|K4fNm<*+sy&`Cz zCM`O^>}_qXdh+!NL_LPDuoaBmUlhC?|8A6~BkA%BkJe{mS*X!;6iZ>)U1SsAK96mF zQv&bW@_|~604jKXZ$*rLO1F@UnK84Xm3}bAA}D|Ds0FoVU6#_YDe6(uf7LmpofAyJ z8VC|2_TScSn||Pc{w7#?d6koP=Clq_r(ATLhlWs<1)ju->BmE#ZF>iq=HG`m1qX{O z8q2?`F}eBv_p5b$9_n!gbqjUtM5dCXlPpv*hopk+5cgwQ4yDGlgQDc)BSeMM8CYt> zAJ)C#Jcjq?`azB%fC--Ac5M0lyvZ_eBa*+(U|KBti+SS{rjq6o8qcD1=3k?^Ev>@2 z_NTWX5OUmW6ut{d+DdQEg~*xcQa@Mr9Id=b<7yozt7c*L*fTf%HOKMdV;Wcg#uAO* zsxwahsG20=Xe$YBmf%Z%r|Sz-lfQ%_BVQ@e%{50YqxiJZ4+woAlC+us(asv8hQWbt znP_%pxarf^n(T3c22#PsG(+Jdjz*GvwF6z{H__F*)>*ClXY8yeUY*r^Kp<%0DMJ%G z*b-|E9rq~udg*hAjkcU7)#=hX(KwEzpq6uQHOf6BN_jH=%KqP7MZ(2v6v%J^~XRfVUW&LU07C?6Y+?+L+VqCZ-3S%CpY}UxISxIe}!+z zM~^p|jL!Og?r_<{{rUAh*xR22=fBD3FC9qjp5=qy8{JLH9un=|H2SYQ`dD!J?w#ku z*2r}igztWQNsLSqeDMvg|08FkP0m06!9Iuu>A|;!Vgh`NYk~9k((vA1@+|o?!KTZe zrRjk__xxndf$q3@EJmw=tvYrQY`3gTZhH*TSLzhOxeP;o4=BS?YCn|OAv*~6*5I6@ z$I~5p^4_;Q);KdR_t-gpM7d$ii%+*Y)OJS4(hwqSJMQKbZMAe ztx>w=baP!Q&wd=8QqB2s?BJysilpm0aL(-R*n)?H1k`}P%CEn=LP#eLald-lNGe{7 z9{NjcS?OssK8_7;nK6azNPL|&#>xplGMq;rey+S~v0=Q^Q2pGDIo_)>x-nDhr9PT~ z!@`Texwh)F14u0S;3wy72D6GV;<>I~x+hLWQI8ym$a!B7IRpgl6(X*ji{{e0Ctgk2 zL>}#Jpn`E()>$F#q}NNFUvK6gv{E3axI}*VK`1_ZbUg>-7$Je1waUu+^xQ0Is%#J4 zj8&K#>dRq&J^`8mXZ<n|OMv!#Z@ zd8sc^pZ)Qhzr&-A6j|26>Jsit!$N&$n}Xilz{YTfw%1flJ=Y%C{_DF$84%v~bPn91 zATM<**(tRnLsF>V5lG=jo!3tJb9fs0<+RRMYfDZ@FA*W)3SSZPM8sDNs6wPfqw;G! z$MeXbg-dyx97XqS@m~u1J)mL9x^z3_`=^8l4|aZGT^{(*+Qz2mKmS46nYkl1#Ez`; zk*yCEL!S$tdMEka-IzhC=46O@7@&FJ$!g9WR;1#XPzKUAWX_#ErsC%ft;Pm-BRq(3 zK8@zaM!3fvlRYSf|ERtRZ9crviJjl4D$bU|th!sl`Fu)$lFU~1ev!hzIpCc7$J`~R zem>c(#3|}%%QyU9Gr1b->k1m~^lTs8_JXi(PzHY%=+cU@$QdXH^R^{D1?K&yXEAUTSXj;*%u@83U5Hfox0I4#A6 z$G<%x4?Q)`lkn|Fl12zz=nV1L{>H-X@QdCu)* z)zQm!pGd7W9LmeN&fDrx%j(HLNmfeqJsqN0zWMvEaJfF(12~|UPA{p-Ji>3hVLRtO z+Tm+{F<9S2a*`@o<;yLh2S=qgcikCxLioeYpGJfPyc=|_<4kH;<>m2n9-L7Rt4S^PfCnB>xBa#O&+ZKXQUT@w;ICidch#Q&7+>@E3qH~)J|l~1 zD1ZI5r5tgvnd42jAarW4;Dk?C$Kbr)^Zf^SI!GMa&g+Yme!!@g`hZHL@=0K@RSLTB z(o<4_D;;XYoT^ zLR}asV3t4T|IXa^Zkv6+#9iERXYTi8s}ETw1MYAH&XXQ>qSqTMC-?6baZ+o}2rsky zcVqLe2l?V-;a1#1vO+jq)%=IvurZgD6>tiLF-7AF2aYyeyAY=| z|JPaRuBq~LiFA{F4ip7lYxPx@MPJYo7=zV#vpy;uRPZTRAqvV`;YRO6U97k)p zb)sY{a9SivW<6ZEAgVoHn*Y)Q2X_IZgIR|3N_92zc6lN5l#k?#ACJ4pyS80EuqLCw zGc%RkaXYVJO?vQf@aLrS+cgeohF_LU1o`)iZ`U4J^UCVhT_?)hRjv7hCXZfQC<)Cp zzhfM`V)wrN3)~W6!1f3nHj}LBlgX@F8|pHlQNJiSJr=7NG)<1+@dd3F0YLe`2w?iF zfd1pJg8Eli2wFYF{in`++I+G5>>Si|M)=(V!W3DPprK2iysZu!C`qhVVFz8T5oMFVklvo_{$GhzE*~cWBlV!cV-Wc2)0HX z_i4P*Ah;ah6{j4?9R&8SIIlO2InW??(^?%vVbSGMiC`f|(ZH3=aX`=G7%k&%WP#h@ z?*DW6Lk^++FaIIwT74^$7k`1U@z4L|KP2tO?6BG@B_p8p>rx#Sk4qcR$(&v?qjZx| z35m8i2HFE8{ABjNSeXI|p*e2JX<;EwFQ{(1U5 z>+EkRz3XknEo2?BvR1KPiz7#Fe|dKE4r#o(T#wYXdR3=}jR(tAxSs{WM;jE}2#E$K z6q1uJw;>Uvym0NAJV*U){5sowd&omI{P{gw%y#<0@wa+V1nzPM>XKMC7XN4yQ>a_di#o# z|54JM==3qw%fohN-y;kg=qC%Guxc913Cj%8$&1=0QK z>yO``vL|`M4^{Q{Gc6h7ZN!gOgfJdtx#z&-_V?4RC#)333=6s`mZ~Z;=#kHbiyLts zWVy4ZLqh+6s~v%VXX=C3BiDsh?Ifw!4U7$`X%A06UM_?!;W;#X8Vv!sYi1*nnRbE_ zABsn#BjsvF9^>uxoAxpPJ$&;~3Yzx({mq^T9_M!VfphdD*yY*WfDQ+3=!6TP-SMo41+G z+R2(D+zkw_lPDGrEo}-LRh&A6?Pa=nc2QKw1M-5^*4};Z%*7mD^lVHXJ&I$AQSz3g zvM2HcKZh_s)q|NAQiC!*SbX6X5eMZZ>24B`3Mrn&20=X~by@W^kQhwKRtpHUpJ42?o%Jp#|_c%iWtpR$3?9(c7PwINuavtoy<$&|K2Bgi zHz*yrF_*y&8jKzA_b%{n)4)IPBnoEeZUA)vxS;4R0)zt$0x*I%^X33K0LuWUKo}kX z|6glY9!}NQ{qHqPB5^aj$`l!r%=0XB5!dj|m`bLL@I{7bFegKX%poEYB2$L+8k2dR zi%gYVN_fxVUgUT2`{P~Dvz~qKzGr>*+WV}%&pv0bv$ndUAUUs}z?0zg_k>Qao={!^ z7VA)+voe*$R1}WXuQvTb_nmhFDr}La_1WxO$QlD=9u5A@H6HfBaT=KRLnME$eFv?f z-pz+Bz-i#!ZGXDdTkQRhFL@Y~WG}MG<>lOy_OS9*+}r|1-PSkBIafuA$2^P=M}ZQY z95SK*-h`{nN4ZWLp*QFnKk(Tv*ko#|Gsd(O zfYOZGm$Ez)W$}bBP)}o->GpmL2x~p;M>(jA+yUN*TRtD=zER7{%@Vv!Of|2D!lySv zql8-WET0=X@z5#Ko$^csQDpX01IVZDvJQ*wwVYyAcoI<^lqL4l2+z9F+rssUsFS8r zm69(^BSt-I3`7CUWnbzikMQJ?KeF%tz}}(cA+AdnR*X)CVODnWIxNqILZh;ZaHG!k z*{f6|{Mr!Z^u08jk$w)eg`#0vM#JlKymvIA+-b-7;lq#;TICI@;@r`HlOiI9NIi3g z^5LMiiU;Vj^_OiQn{j%ysWTzvwCgOJNdD6jLJX^Yh<8azPii#zx8SSv$J||DxtBvH z?uTiPxF=JRE0s~uZFiFTb3eTBwmS>DCo_UhB-1Ose`APujTsgPQ6s zZK?5mvf%A}5DHbL^JzI`(ols+Um1t4r`^v}&qOf7r zVLQ{Hvaqx!zp@0{V6DqPvlz(}5yH8W9_!@gV%9mEdFll0+-6LtgRBmi0pkgeyyYWY zs{UY8X zJ6xpJCeItlYH!AGV8SKj*vxHnL9z1H+529T2PqXkJr!hs#LlE%=wlO%!6Wieol z4(hKENZ@KyPq$yU2ujf}SCt`H7S1$tbSTU}2KR=!Xe-lT`cogzI1pvm-RLh3Hh-M2 zULlMg$?7^)AG~(!7@kC%>D!gb99^D|mAGW8k1zbPw<|58I}~LTYF__**^kmVbsAjV zmn93`#R`ORACgi6+_F88JQ*qun^DaUZkJ<4iaX&?{;#h8pB&(>b|B#$_%GlN$#~P% zkR;02_P&&**Sr!3L%dv5(7CY;mB{!yq=4%eR~9FJo}fV}LRsxcq*+!vef&D}h(E(; zqGEQ)@r6+?g);^GZo)w%q~`EscjrMoQw34YUv^WcTEiaU2>1_cRdJfX6VT7OCg>+O z{%OqOj#WEr>w2stELvMEvObfttYNz74;M6z*|>XUBG`4DXHo4I*B*gd->}nE%g*fSQRL1GJPhNd$SlJ0K9_Q@$yQY@Nd#k@G$`jc&*foM_ZWm|R& zS;xfVEx|TC$@EKui|^M3C8OBf9J?=`96;*ogyNghC!f8h*zS+`6g znWwhpiZg;>zytOyUU1gsjqdAOR#aTu?=_|wLS!>RV>t4}@t)LQ4yYINj24ayKWj$) zpy8)v^#}0d&%b<95cnKfA0rXH-WKO(AUFr(oOWr~sz#)%S4|CDe1Kf|~hU58ixlnrPm3t+@8nA-J zY!Ty$48^WBQC0{XoiQT)uo1`a5k5^?!u_}fFL{IqBi0xS;^^}Mmk{5D)H{u>bfYwH zajvonkxjrQw!Ol}X5QgEO$r^S zTD)~{BTeTg!8xBAY#667MFT0+XfpH$us0MLI&b1UZ|B1$qTi(rHD!4fwZ$L5gbZTC z*qMz$rQ{@4=iau;Kqj|L2Qtw-6(QirIbkFIj^z&yRp;q%fzsG8cAng!B+j-fwyTS>0Ot`N`jjBjYL(2~Fk2>+)9sL6ZatpB zQ?ojhucXcWj@41;--31!1@}-CLwfEQOY?9=G8P=uIf10db`OS8uk4Ce8nVnAU)qei zgD7~1qL_EAeSbYXiE8YqZbJ6`>R`WLGGlqiVc~|wXv7WJ>s%0p;82v{!G%>CU&xP6 zW8#dPxJ33tqSm=<3n9ctOogT*_I%PH3jU!e^RF&Bck6W+TtQWj%ZYZaU!?ob_WSGI z_Mr-(Z4r0t(vn%`tIYZN`Gnfru|VN>>&s;G{qJ*_Y>%?nfm1Pzk=rK)uJD0`v%n6E zwnv1)0+?OM83qZnx!30!cH1E~3uY2>?^Ie}Kt#~IzFWdrOyc1^v!TM}X}#f#O>%qE zL{Sh>%p0Sb{pmY@-KtNFCtU!}x@B(K5vb{PU##zowrwA%!l?*23*Fe_T{gW)8X*T) zq=6Itg_s0y?dk0=T9kpaFnrcSF+-gN>6c(kp%}3tOi*Lh|I-Q8qI!=)g3MFKGJP?% z6j7qea+|}7hN~DqrD~urG;FW#@&F!Y>;ta#{h1ZjG*25cNE^CAQFs~ys{#Qn$5%BG zD+HxwxrSL--_czz|QC#7Cjv32*MOkzffvKh6^@S)iu$7L7E(u7`KD~I58T3es z5)*L1gEX{{$hWRFCbo*~0t{$3z9Pvw6d&XI$cXCqh+$^~L-EO%-N5Yav_y%tq?~{Z=!^`Zm zvD;($ZVh+3f}p}BO*K?iCKU1leZyPf4tW=~N_yq}8_qy#`)`prX^9U&+5e$O-S(<3 z0Ei*bP8`pi3tqY00m)V@kuAXYDFR|MWFk_A4 zP8p{);P3^z+#u+P#vX74BM3qI`9CMiU+-wf!z(?STog z<ieQ%Z=dsYh$_H=kDZUJOtJbO!^E(7OSz_fUIXdGR#;Zd<|gJR6;u6)znzY?9}8 y^hWQOvyq+zzg5PmkQFjj;TMxn{johaZm%S$H}$q8E+2s&%%?(-8sdb0TKq4Df_-!V literal 0 HcmV?d00001 diff --git a/core/vm/evm.go b/core/vm/evm.go index b45a434545..5a445dba7f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -76,6 +76,7 @@ type TxContext struct { BlobHashes []common.Hash // Provides information for BLOBHASH BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set AccessEvents *state.AccessEvents // Capture all state accesses for this tx + Index uint64 // the index of the transaction within the block being executed (0 if executing a standalone call) } // EVM is the Ethereum Virtual Machine base object and provides @@ -461,7 +462,9 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui // - the storage is non-empty contractHash := evm.StateDB.GetCodeHash(address) storageRoot := evm.StateDB.GetStorageRoot(address) - if evm.StateDB.GetNonce(address) != 0 || + targetNonce := evm.StateDB.GetNonce(address) + + if targetNonce != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code (storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 8a82de5d8b..779d9994c4 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -681,7 +681,7 @@ func TestCreate2Addresses(t *testing.T) { stack.push(big.NewInt(0)) // memstart stack.push(big.NewInt(0)) // value gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) - fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) + fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.PrettyPrint()) */ expected := common.BytesToAddress(common.FromHex(tt.expected)) if !bytes.Equal(expected.Bytes(), address.Bytes()) { diff --git a/core/vm/interface.go b/core/vm/interface.go index 42a72db482..90f0337980 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -97,8 +97,12 @@ type StateDB interface { Witness() *stateless.Witness + //BlockAccessList() *types.BlockAccessList + AccessEvents() *state.AccessEvents + TxIndex() int + // Finalise must be invoked at the end of a transaction Finalise(bool) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 34d19008da..dbe190fcca 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -35,6 +35,7 @@ type Config struct { ExtraEips []int // Additional EIPS that are to be enabled StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) + BALConstruction bool // Generate BAL for executed blocks (testing purposes) } // ScopeContext contains the things that are per-call, such as stack and memory, diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index 89a2ebf6f4..bceae63848 100644 --- a/core/vm/jump_table_export.go +++ b/core/vm/jump_table_export.go @@ -28,6 +28,8 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) { switch { case rules.IsVerkle: return newCancunInstructionSet(), errors.New("verkle-fork not defined yet") + case rules.IsGlamsterdam: + return newPragueInstructionSet(), errors.New("glamsterdam-fork not defined yet") case rules.IsOsaka: return newOsakaInstructionSet(), nil case rules.IsPrague: diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index d75a5b0459..a9e194ebab 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -669,7 +669,7 @@ func TestColdAccountAccessCost(t *testing.T) { 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) + //t.Logf("%d: %v %d", step, vm.OpCode(op).PrettyPrint(), cost) if step == tc.step { have = cost } diff --git a/eth/api_debug.go b/eth/api_debug.go index 188dee11aa..0250029d0e 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -17,11 +17,14 @@ package eth import ( + "bytes" "context" "errors" "fmt" "time" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/rawdb" @@ -443,3 +446,29 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) { } return api.eth.blockchain.GetTrieFlushInterval().String(), nil } + +func (api *DebugAPI) GetBlockAccessList(number rpc.BlockNumberOrHash) (*bal.BlockAccessList, error) { + var block *types.Block + if num := number.BlockNumber; num != nil { + block = api.eth.blockchain.GetBlockByNumber(uint64(num.Int64())) + } else if hash := number.BlockHash; hash != nil { + block = api.eth.blockchain.GetBlockByHash(*hash) + } + + if block == nil { + return nil, fmt.Errorf("block not found") + } + return block.Body().AccessList, nil +} + +func (api *DebugAPI) GetEncodedAccessList(number rpc.BlockNumberOrHash) ([]byte, error) { + bal, err := api.GetBlockAccessList(number) + if err != nil { + return nil, err + } + var enc bytes.Buffer + if err = bal.EncodeRLP(&enc); err != nil { + return nil, err + } + return enc.Bytes(), nil +} diff --git a/eth/backend.go b/eth/backend.go index 80ffd301ce..f15749b9e7 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -220,6 +220,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) } } + var buildBAL bool + if config.BuildBAL != nil { + buildBAL = *config.BuildBAL + } var ( options = &core.BlockChainConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -235,6 +239,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), VmConfig: vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + BALConstruction: buildBAL, }, } ) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 409899d582..022d6086d9 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -748,7 +748,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil } log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number()) - proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness) + proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness, true) if err != nil { log.Warn("NewPayload: inserting block failed", "error", err) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 97d23744a0..b2b1617345 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -163,6 +163,8 @@ type Config struct { // OverrideVerkle (TODO: remove after the fork) OverrideVerkle *uint64 `toml:",omitempty"` + + BuildBAL *bool `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/tracers/js/bigint.go b/eth/tracers/js/bigint.go index 9aeb330420..02f005f9b1 100644 --- a/eth/tracers/js/bigint.go +++ b/eth/tracers/js/bigint.go @@ -17,4 +17,4 @@ package js // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. -const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt` +const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(PrettyPrint(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(PrettyPrint(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=PrettyPrint(v[--l]),zeros="0000000",digit;while(--l>=0){digit=PrettyPrint(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return PrettyPrint(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt` diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a6d93fc1c5..bb0bcbbed4 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -468,6 +468,11 @@ web3._extend({ call: 'debug_getTrieFlushInterval', params: 0 }), + new web3._extend.Method({ + name: 'getBlockAccessList', + call: 'debug_getBlockAccessList', + params: 1 + }), ], properties: [] }); diff --git a/params/config.go b/params/config.go index 58a0550303..2d62cba432 100644 --- a/params/config.go +++ b/params/config.go @@ -406,11 +406,12 @@ type ChainConfig struct { // Fork scheduling was switched from blocks to timestamps here - ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai) - CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun) - PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) - OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka) - VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) + ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai) + CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun) + PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) + OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka) + GlamsterdamTime *uint64 `json:"glamsterdamTime,omitempty"` // Glamsterdam switch time (nil = no fork, 0 = already on glamsterdam) + VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. @@ -649,6 +650,11 @@ func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time) } +// IsGlamsterdam returns whether time is either equal to the Glamsterdam fork time or greater. +func (c *ChainConfig) IsGlamsterdam(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.GlamsterdamTime, time) +} + // IsVerkle returns whether time is either equal to the Verkle fork time or greater. func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) @@ -1062,13 +1068,13 @@ 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 - IsEIP2929, IsEIP4762 bool - IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin, IsLondon bool - IsMerge, IsShanghai, IsCancun, IsPrague, IsOsaka bool - IsVerkle bool + ChainID *big.Int + IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool + IsEIP2929, IsEIP4762 bool + IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool + IsBerlin, IsLondon bool + IsMerge, IsShanghai, IsCancun, IsPrague, IsOsaka, IsGlamsterdam bool + IsVerkle bool } // Rules ensures c's ChainID is not nil. @@ -1098,6 +1104,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsCancun: isMerge && c.IsCancun(num, timestamp), IsPrague: isMerge && c.IsPrague(num, timestamp), IsOsaka: isMerge && c.IsOsaka(num, timestamp), + IsGlamsterdam: isMerge && c.IsGlamsterdam(num, timestamp), IsVerkle: isVerkle, IsEIP4762: isVerkle, } diff --git a/tests/block_test.go b/tests/block_test.go index 91d9f2e653..a3e5e06659 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -105,7 +105,7 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) { } for _, snapshot := range snapshotConf { for _, dbscheme := range dbschemeConf { - if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil { + if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, true, nil, nil)); err != nil { t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err) return } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index a76037405b..e33bbe84f1 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -111,7 +111,7 @@ type btHeaderMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 } -func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { +func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, bal bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { config, ok := Forks[t.json.Network] if !ok { return UnsupportedForkError{t.json.Network} @@ -159,6 +159,7 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t VmConfig: vm.Config{ Tracer: tracer, StatelessSelfValidation: witness, + BALConstruction: bal, }, } if snapshotter {