diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 0ae5a3b8f1f..36a17913527 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" ) var _ = (*executableDataMarshaling)(nil) @@ -34,6 +35,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + BlockAccessList *bal.BlockAccessList `json:"blockAccessList"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var enc ExecutableData @@ -59,6 +61,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.Withdrawals = e.Withdrawals enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) + enc.BlockAccessList = e.BlockAccessList enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } @@ -83,6 +86,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + BlockAccessList *bal.BlockAccessList `json:"blockAccessList"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var dec ExecutableData @@ -157,6 +161,9 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExcessBlobGas != nil { e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) } + if dec.BlockAccessList != nil { + e.BlockAccessList = dec.BlockAccessList + } if dec.ExecutionWitness != nil { e.ExecutionWitness = dec.ExecutionWitness } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index ddb276ab091..4a6272fb190 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -18,6 +18,7 @@ package engine import ( "fmt" + "github.com/ethereum/go-ethereum/core/types/bal" "math/big" "slices" @@ -50,6 +51,7 @@ var ( // ExecutionPayloadV3 has the syntax of ExecutionPayloadV2 and appends the new // fields: blobGasUsed and excessBlobGas. PayloadV3 PayloadVersion = 0x3 + PayloadV4 PayloadVersion = 0x4 ) //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go @@ -90,6 +92,7 @@ type ExecutableData struct { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *uint64 `json:"blobGasUsed"` ExcessBlobGas *uint64 `json:"excessBlobGas"` + BlockAccessList *bal.BlockAccessList `json:"blockAccessList"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } @@ -293,30 +296,39 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H requestsHash = &h } + var blockAccessListHash *common.Hash + body := types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals} + if data.BlockAccessList != nil { + body.AccessList = data.BlockAccessList + balHash := data.BlockAccessList.Hash() + blockAccessListHash = &balHash + } + header := &types.Header{ - ParentHash: data.ParentHash, - UncleHash: types.EmptyUncleHash, - Coinbase: data.FeeRecipient, - Root: data.StateRoot, - TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), - ReceiptHash: data.ReceiptsRoot, - Bloom: types.BytesToBloom(data.LogsBloom), - Difficulty: common.Big0, - Number: new(big.Int).SetUint64(data.Number), - GasLimit: data.GasLimit, - GasUsed: data.GasUsed, - Time: data.Timestamp, - BaseFee: data.BaseFeePerGas, - Extra: data.ExtraData, - MixDigest: data.Random, - WithdrawalsHash: withdrawalsRoot, - ExcessBlobGas: data.ExcessBlobGas, - BlobGasUsed: data.BlobGasUsed, - ParentBeaconRoot: beaconRoot, - RequestsHash: requestsHash, + ParentHash: data.ParentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: data.FeeRecipient, + Root: data.StateRoot, + TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), + ReceiptHash: data.ReceiptsRoot, + Bloom: types.BytesToBloom(data.LogsBloom), + Difficulty: common.Big0, + Number: new(big.Int).SetUint64(data.Number), + GasLimit: data.GasLimit, + GasUsed: data.GasUsed, + Time: data.Timestamp, + BaseFee: data.BaseFeePerGas, + Extra: data.ExtraData, + MixDigest: data.Random, + WithdrawalsHash: withdrawalsRoot, + ExcessBlobGas: data.ExcessBlobGas, + BlobGasUsed: data.BlobGasUsed, + ParentBeaconRoot: beaconRoot, + RequestsHash: requestsHash, + BlockAccessListHash: blockAccessListHash, } return types.NewBlockWithHeader(header). - WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). + WithBody(body). WithWitness(data.ExecutionWitness), nil } @@ -343,6 +355,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. BlobGasUsed: block.BlobGasUsed(), ExcessBlobGas: block.ExcessBlobGas(), ExecutionWitness: block.ExecutionWitness(), + BlockAccessList: block.Body().AccessList, } // Add blobs. diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index f6538b13567..487a38ec0dc 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.PathScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) { + if err := tests[name].Run(false, rawdb.PathScheme, 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/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5303d432fb6..e61442cf95a 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -316,11 +316,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } // EIP-7002 - if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { + if _, _, err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err)) } // EIP-7251 - if err := core.ProcessConsolidationQueue(&requests, evm); err != nil { + if _, _, err := core.ProcessConsolidationQueue(&requests, evm); err != nil { return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err)) } } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index cc294b2f309..bb23f75ab3c 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -155,6 +155,7 @@ var ( utils.BeaconGenesisTimeFlag, utils.BeaconCheckpointFlag, utils.BeaconCheckpointFileFlag, + utils.ExperimentalBALFlag, }, utils.NetworkFlags, utils.DatabaseFlags) rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 0c5db9e6d8b..f685799e67d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1009,6 +1009,14 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } + + // Block Access List flags + + ExperimentalBALFlag = &cli.BoolFlag{ + Name: "experimental.bal", + Usage: "Enable generation of EIP-7928 block access lists when importing post-Cancun blocks which lack them. When this flag is specified, importing blocks containing access lists triggers validation of their correctness and execution based off them. The header block access list field is not set with blocks created when this flag is specified, nor is it validated when importing blocks that contain access lists. This is used for development purposes only. Do not enable it otherwise.", + Category: flags.MiscCategory, + } ) var ( @@ -1917,6 +1925,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name) } } + + cfg.ExperimentalBAL = ctx.Bool(ExperimentalBALFlag.Name) } // MakeBeaconLightConfig constructs a beacon light client config based on the @@ -2319,6 +2329,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } options.VmConfig = vmcfg + options.EnableBALForTesting = ctx.Bool(ExperimentalBALFlag.Name) chain, err := core.NewBlockChain(chainDb, gspec, engine, options) if err != nil { Fatalf("Can't create BlockChain: %v", err) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 84926c3d0b6..442c60ade40 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -272,6 +272,9 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa return err } } + if chain.Config().IsAmsterdam(header.Number, header.Time) && header.BlockAccessListHash == nil { + return fmt.Errorf("block access list hash must be set post-Amsterdam") + } return nil } @@ -343,9 +346,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, setting the final state and // assembling the block. -func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error) { if !beacon.IsPoSHeader(header) { - return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts) + return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts, onFinalization) } shanghai := chain.Config().IsShanghai(header.Number, header.Time) if shanghai { @@ -364,6 +367,10 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(true) + if onFinalization != nil { + onFinalization() + } + // Assemble the final block. block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index b593d2117d2..6e01465a9d4 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -579,7 +579,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. -func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) { if len(body.Withdrawals) > 0 { return nil, errors.New("clique does not support withdrawals") } @@ -589,6 +589,10 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if onFinalize != nil { + onFinalize() + } + // Assemble and return the final block for sealing. return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil } diff --git a/consensus/consensus.go b/consensus/consensus.go index a68351f7ffa..e1120af36d3 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -92,7 +92,7 @@ type Engine interface { // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) + FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error) // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 4f92f1282b9..0ef146b2541 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -511,7 +511,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. -func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) { if len(body.Withdrawals) > 0 { return nil, errors.New("ethash does not support withdrawals") } @@ -521,6 +521,9 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if onFinalize != nil { + onFinalize() + } // Header seems complete, assemble into a block and return return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil } diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index e14d1295613..af3f3ace6aa 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -73,6 +73,8 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig { bc = s.BPO2 case cfg.IsBPO1(london, time) && s.BPO1 != nil: bc = s.BPO1 + case cfg.IsAmsterdam(london, time) && s.Amsterdam != nil: + bc = s.Amsterdam case cfg.IsOsaka(london, time) && s.Osaka != nil: bc = s.Osaka case cfg.IsPrague(london, time) && s.Prague != nil: diff --git a/core/block_access_list_tracer.go b/core/block_access_list_tracer.go new file mode 100644 index 00000000000..e3d09949e80 --- /dev/null +++ b/core/block_access_list_tracer.go @@ -0,0 +1,102 @@ +package core + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/holiman/uint256" + "math/big" +) + +// BlockAccessListTracer is a tracer which gathers state accesses/mutations +// from the execution of a block. It is used for constructing and verifying +// EIP-7928 block access lists. +type BlockAccessListTracer struct { + builder *bal.AccessListBuilder + + // the access list index that changes are currently being recorded into + balIdx uint16 +} + +// NewBlockAccessListTracer returns an BlockAccessListTracer and a set of hooks +func NewBlockAccessListTracer() (*BlockAccessListTracer, *tracing.Hooks) { + balTracer := &BlockAccessListTracer{ + builder: bal.NewAccessListBuilder(), + } + hooks := &tracing.Hooks{ + OnBlockFinalization: balTracer.OnBlockFinalization, + OnPreTxExecutionDone: balTracer.OnPreTxExecutionDone, + OnTxEnd: balTracer.TxEndHook, + OnEnter: balTracer.OnEnter, + OnExit: balTracer.OnExit, + OnCodeChangeV2: balTracer.OnCodeChange, + OnBalanceChange: balTracer.OnBalanceChange, + OnNonceChangeV2: balTracer.OnNonceChange, + OnStorageChange: balTracer.OnStorageChange, + OnStorageRead: balTracer.OnStorageRead, + OnAccountRead: balTracer.OnAcountRead, + OnSelfDestructChange: balTracer.OnSelfDestruct, + } + wrappedHooks, _ := tracing.WrapWithJournal(hooks) + return balTracer, wrappedHooks +} + +// AccessList returns the constructed access list. +// It is assumed that this is only called after all the block state changes +// have been executed and the block has been finalized. +func (a *BlockAccessListTracer) AccessList() *bal.AccessListBuilder { + return a.builder +} + +func (a *BlockAccessListTracer) OnPreTxExecutionDone() { + a.builder.FinaliseIdxChanges(0) + a.balIdx++ +} + +func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) { + a.builder.FinaliseIdxChanges(a.balIdx) + a.balIdx++ +} + +func (a *BlockAccessListTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + a.builder.EnterScope() +} + +func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + a.builder.ExitScope(reverted) +} + +func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason tracing.CodeChangeReason) { + a.builder.CodeChange(addr, prevCode, code) +} + +func (a *BlockAccessListTracer) OnSelfDestruct(addr common.Address) { + a.builder.SelfDestruct(addr) +} + +func (a *BlockAccessListTracer) OnBlockFinalization() { + a.builder.FinaliseIdxChanges(a.balIdx) +} + +func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) { + newU256 := new(uint256.Int).SetBytes(newBalance.Bytes()) + prevU256 := new(uint256.Int).SetBytes(prevBalance.Bytes()) + a.builder.BalanceChange(addr, prevU256, newU256) +} + +func (a *BlockAccessListTracer) OnNonceChange(addr common.Address, prev uint64, new uint64, reason tracing.NonceChangeReason) { + a.builder.NonceChange(addr, prev, new) +} + +func (a *BlockAccessListTracer) OnStorageRead(addr common.Address, key common.Hash) { + a.builder.StorageRead(addr, key) +} + +func (a *BlockAccessListTracer) OnAcountRead(addr common.Address) { + a.builder.AccountRead(addr) +} + +func (a *BlockAccessListTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) { + a.builder.StorageWrite(addr, slot, prev, new) +} diff --git a/core/block_validator.go b/core/block_validator.go index 008444fbbc4..4b6284a0022 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -19,7 +19,6 @@ package core import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -111,6 +110,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } } + // block access lists must be present after the Amsterdam hard fork + if v.config.IsAmsterdam(block.Number(), block.Time()) { + if block.Body().AccessList == nil { + return fmt.Errorf("access list not present in block body") + } else if *block.Header().BlockAccessListHash != block.Body().AccessList.Hash() { + return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.Body().AccessList.Hash(), *block.Header().BlockAccessListHash) + } else if err := block.Body().AccessList.Validate(); err != nil { + return fmt.Errorf("invalid block access list: %v", err) + } + } else if !v.bc.cfg.EnableBALForTesting { + // if --experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists. + if block.Body().AccessList != nil { + return fmt.Errorf("access list not allowed in block body if not in amsterdam or --experimental.bal is set") + } else if block.Header().BlockAccessListHash != nil { + return fmt.Errorf("access list hash in block header not allowed when --experimental.bal is set") + } + } else { + // if --experimental.bal is enabled, the BAL hash is not allowed in the header. + // this is in order that Geth can import pre-existing chains augmented with BALs + // and not have a hash mismatch. + if block.Header().BlockAccessListHash != nil { + return fmt.Errorf("access list hash in block header not allowed pre-amsterdam") + } + } + // Ancestor block must be known. if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { @@ -123,7 +147,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // ValidateState validates the various changes that happen after a state transition, // such as amount of used gas, the receipt roots and the state root itself. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, validateStateRoot, stateless bool) error { if res == nil { return errors.New("nil ProcessResult value") } @@ -160,10 +184,13 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } else if res.Requests != nil { return errors.New("block has requests before prague fork") } - // Validate the state root against the received state root and throw - // an error if they don't match. - if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { - return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) + + if validateStateRoot { + // Validate the state root against the received state root and throw + // an error if they don't match. + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) + } } return nil } diff --git a/core/blockchain.go b/core/blockchain.go index b7acd12aca7..f1b77d45b90 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -98,6 +98,13 @@ var ( blockExecutionTimer = metrics.NewRegisteredResettingTimer("chain/execution", nil) blockWriteTimer = metrics.NewRegisteredResettingTimer("chain/write", nil) + // BAL-specific timers + blockPreprocessingTimer = metrics.NewRegisteredResettingTimer("chain/preprocess", nil) + blockPrestateLoadTimer = metrics.NewRegisteredResettingTimer("chain/prestateload", nil) + txExecutionTimer = metrics.NewRegisteredResettingTimer("chain/txexecution", nil) + stateRootCalctimer = metrics.NewRegisteredResettingTimer("chain/rootcalculation", nil) + blockPostprocessingTimer = metrics.NewRegisteredResettingTimer("chain/postprocess", nil) + blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) @@ -196,6 +203,11 @@ type BlockChainConfig struct { // If the value is -1, indexing is disabled. TxLookupLimit int64 + // If EnableBALForTesting is enabled, block access lists will be created + // from block execution and embedded in the body. The block access list + // hash will not be set in the header. + EnableBALForTesting bool + // StateSizeTracking indicates whether the state size tracking is enabled. StateSizeTracking bool } @@ -330,14 +342,14 @@ type BlockChain struct { stopping atomic.Bool // false if chain is running, true when stopped procInterrupt atomic.Bool // interrupt signaler for block processing - engine consensus.Engine - validator Validator // Block and state validator interface - prefetcher Prefetcher - processor Processor // Block transaction processor interface - logger *tracing.Hooks - stateSizer *state.SizeTracker // State size tracking - - lastForkReadyAlert time.Time // Last time there was a fork readiness print out + engine consensus.Engine + validator Validator // Block and state validator interface + prefetcher Prefetcher + processor Processor // Block transaction processor interface + parallelProcessor ParallelStateProcessor + logger *tracing.Hooks + stateSizer *state.SizeTracker // State size tracking + lastForkReadyAlert time.Time // Last time there was a fork readiness print out } // NewBlockChain returns a fully initialised block chain using information @@ -395,6 +407,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, bc.validator = NewBlockValidator(chainConfig, bc) bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc) bc.processor = NewStateProcessor(bc.hc) + bc.parallelProcessor = NewParallelStateProcessor(bc.hc, bc.GetVMConfig()) genesisHeader := bc.GetHeaderByNumber(0) if genesisHeader == nil { @@ -1911,7 +1924,13 @@ 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) + + enableBAL := (bc.cfg.EnableBALForTesting && bc.chainConfig.IsCancun(block.Number(), block.Time())) || bc.chainConfig.IsAmsterdam(block.Number(), block.Time()) + blockHasAccessList := block.Body().AccessList != nil + constructBAL := enableBAL && !blockHasAccessList + validateBAL := enableBAL && blockHasAccessList + + res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, constructBAL, validateBAL) if err != nil { return nil, it.index, err } @@ -1983,7 +2002,7 @@ func (bpr *blockProcessingResult) Witness() *stateless.Witness { // 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, constructBALForTesting bool, validateBAL bool) (_ *blockProcessingResult, blockEndErr error) { var ( err error startTime = time.Now() @@ -1994,6 +2013,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s if bc.cfg.NoPrefetch { statedb, err = state.New(parentRoot, bc.statedb) + if err != nil { return nil, err } @@ -2033,7 +2053,10 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s // Disable tracing for prefetcher executions. vmCfg := bc.cfg.VmConfig vmCfg.Tracer = nil - bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt) + if block.Body().AccessList == nil { + // only use the state prefetcher for non-BAL blocks. + bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt) + } blockPrefetchExecuteTimer.Update(time.Since(start)) if interrupt.Load() { @@ -2062,8 +2085,15 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s witnessStats = stateless.NewWitnessStats() } } - statedb.StartPrefetcher("chain", witness, witnessStats) - defer statedb.StopPrefetcher() + + // access-list containing blocks don't use the prefetcher because + // state root computation proceeds concurrently with transaction + // execution, meaning the prefetcher doesn't have any time to run + // before the trie nodes are needed for state root computation. + if block.Body().AccessList == nil { + statedb.StartPrefetcher("chain", witness, witnessStats) + defer statedb.StopPrefetcher() + } } if bc.logger != nil && bc.logger.OnBlockStart != nil { @@ -2079,21 +2109,82 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s }() } - // Process block using the parent state as reference point - pstart := time.Now() - res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig) - if err != nil { - bc.reportBlock(block, res, err) - return nil, err - } - ptime := time.Since(pstart) + blockHadBAL := block.Body().AccessList != nil + var res *ProcessResult + var resWithMetrics *ProcessResultWithMetrics + var ptime, vtime time.Duration + if block.Body().AccessList != nil { + if block.NumberU64() == 0 { + return nil, fmt.Errorf("genesis block cannot have a block access list") + } + // TODO: rename 'validateBAL' to indicate that it's for validating that the BAL + // is present and we are after amsterdam fork. validateBAL=false is only used for + // testing BALs in pre-Amsterdam blocks. + if !validateBAL && !bc.chainConfig.IsAmsterdam(block.Number(), block.Time()) { + bc.reportBlock(block, res, fmt.Errorf("received block containing access list before glamsterdam activated")) + return nil, err + } + // Process block using the parent state as reference point + pstart := time.Now() + resWithMetrics, err = bc.parallelProcessor.Process(block, statedb, bc.cfg.VmConfig) + if err != nil { + // TODO: okay to pass nil here as execution result? + bc.reportBlock(block, nil, err) + return nil, err + } + ptime = time.Since(pstart) - vstart := time.Now() - if err := bc.validator.ValidateState(block, statedb, res, false); err != nil { - bc.reportBlock(block, res, err) - return nil, err + vstart := time.Now() + var err error + err = bc.validator.ValidateState(block, statedb, resWithMetrics.ProcessResult, false, false) + if err != nil { + // TODO: okay to pass nil here as execution result? + bc.reportBlock(block, nil, err) + return nil, err + } + res = resWithMetrics.ProcessResult + vtime = time.Since(vstart) + } else { + var balTracer *BlockAccessListTracer + // Process block using the parent state as reference point + if constructBALForTesting { + balTracer, bc.cfg.VmConfig.Tracer = NewBlockAccessListTracer() + } + // Process block using the parent state as reference point + pstart := time.Now() + res, err = bc.processor.Process(block, statedb, bc.cfg.VmConfig) + if err != nil { + bc.reportBlock(block, res, err) + return nil, err + } + ptime = time.Since(pstart) + + // TODO: if I remove this check before executing balTracer.Finalise, the following test fails: + // ExecutionSpecBlocktests/shanghai/eip3855_push0/push0/push0_storage_overwrite.json + if constructBALForTesting { + balTracer.OnBlockFinalization() + } + + // unset the BAL-creation tracer (dirty) + bc.cfg.VmConfig.Tracer = nil + + vstart := time.Now() + if err := bc.validator.ValidateState(block, statedb, res, true, false); err != nil { + bc.reportBlock(block, res, err) + return nil, err + } + vtime = time.Since(vstart) + + if constructBALForTesting { + // 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 = balTracer.AccessList().ToEncodingObj() + block = block.WithBody(*existingBody) + } } - vtime := time.Since(vstart) // If witnesses was generated and stateless self-validation requested, do // that now. Self validation should *never* run in production, it's more of @@ -2124,26 +2215,37 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s } } - xvtime := time.Since(xvstart) - proctime := time.Since(startTime) // processing + validation + cross validation + var proctime time.Duration + if blockHadBAL { + blockPreprocessingTimer.Update(resWithMetrics.PreProcessTime) + blockPrestateLoadTimer.Update(resWithMetrics.PrestateLoadTime) + txExecutionTimer.Update(resWithMetrics.ExecTime) + stateRootCalctimer.Update(resWithMetrics.RootCalcTime) + blockPostprocessingTimer.Update(resWithMetrics.PostProcessTime) - // Update the metrics touched during block processing and validation - accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) - storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) - if statedb.AccountLoaded != 0 { - accountReadSingleTimer.Update(statedb.AccountReads / time.Duration(statedb.AccountLoaded)) - } - if statedb.StorageLoaded != 0 { - storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded)) + accountHashTimer.Update(statedb.AccountHashes) + } else { + xvtime := time.Since(xvstart) + proctime = time.Since(startTime) // processing + validation + cross validation + + // Update the metrics touched during block processing and validation + accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) + storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) + if statedb.AccountLoaded != 0 { + accountReadSingleTimer.Update(statedb.AccountReads / time.Duration(statedb.AccountLoaded)) + } + if statedb.StorageLoaded != 0 { + storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded)) + } + accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) + storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) + accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) + triehash := statedb.AccountHashes // The time spent on tries hashing + trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update + blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing + blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation + blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation } - accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) - storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) - accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) - triehash := statedb.AccountHashes // The time spent on tries hashing - trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update - blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing - blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation - blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation // Write the block to the chain and get the status. var ( diff --git a/core/chain_makers.go b/core/chain_makers.go index af55716cca3..386003e706a 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -410,7 +410,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals} - block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts) + block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts, nil) if err != nil { panic(err) } @@ -520,7 +520,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine Uncles: b.uncles, Withdrawals: b.withdrawals, } - block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts) + block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts, nil) if err != nil { panic(err) } diff --git a/core/gen_genesis.go b/core/gen_genesis.go index 2028f98edc0..1255ff12c9a 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -5,6 +5,7 @@ package core import ( "encoding/json" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -19,21 +20,22 @@ var _ = (*genesisSpecMarshaling)(nil) // MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce math.HexOrDecimal64 `json:"nonce"` - Timestamp math.HexOrDecimal64 `json:"timestamp"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash common.Hash `json:"mixHash"` - Coinbase common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` - Number math.HexOrDecimal64 `json:"number"` - GasUsed math.HexOrDecimal64 `json:"gasUsed"` - ParentHash common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` - BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + Config *params.ChainConfig `json:"config"` + Nonce math.HexOrDecimal64 `json:"nonce"` + Timestamp math.HexOrDecimal64 `json:"timestamp"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash common.Hash `json:"mixHash"` + Coinbase common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` + Number math.HexOrDecimal64 `json:"number"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + ParentHash common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` + BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` } var enc Genesis enc.Config = g.Config @@ -56,27 +58,29 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee) enc.ExcessBlobGas = (*math.HexOrDecimal64)(g.ExcessBlobGas) enc.BlobGasUsed = (*math.HexOrDecimal64)(g.BlobGasUsed) + enc.BlockAccessListHash = g.BlockAccessListHash return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - Timestamp *math.HexOrDecimal64 `json:"timestamp"` - ExtraData *hexutil.Bytes `json:"extraData"` - GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash *common.Hash `json:"mixHash"` - Coinbase *common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"number"` - GasUsed *math.HexOrDecimal64 `json:"gasUsed"` - ParentHash *common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` - BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + Config *params.ChainConfig `json:"config"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + Timestamp *math.HexOrDecimal64 `json:"timestamp"` + ExtraData *hexutil.Bytes `json:"extraData"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash *common.Hash `json:"mixHash"` + Coinbase *common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"number"` + GasUsed *math.HexOrDecimal64 `json:"gasUsed"` + ParentHash *common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` + BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -133,5 +137,9 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { if dec.BlobGasUsed != nil { g.BlobGasUsed = (*uint64)(dec.BlobGasUsed) } + fmt.Printf("dec al hash is %v\n", dec.BlockAccessListHash) + if dec.BlockAccessListHash != nil { + g.BlockAccessListHash = dec.BlockAccessListHash + } return nil } diff --git a/core/genesis.go b/core/genesis.go index 2fd044c70a7..932410cdfdb 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -67,12 +67,13 @@ type Genesis struct { // These fields are used for consensus tests. Please don't use them // in actual genesis blocks. - Number uint64 `json:"number"` - GasUsed uint64 `json:"gasUsed"` - ParentHash common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559 - ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844 - BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844 + Number uint64 `json:"number"` + GasUsed uint64 `json:"gasUsed"` + ParentHash common.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559 + ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844 + BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844 + BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` // EIP-7928 } // copy copies the genesis. @@ -122,6 +123,7 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) { genesis.BaseFee = genesisHeader.BaseFee genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas genesis.BlobGasUsed = genesisHeader.BlobGasUsed + genesis.BlockAccessListHash = genesisHeader.BlockAccessListHash return &genesis, nil } @@ -469,18 +471,19 @@ func (g *Genesis) ToBlock() *types.Block { // toBlockWithRoot constructs the genesis block with the given genesis state root. func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { head := &types.Header{ - Number: new(big.Int).SetUint64(g.Number), - Nonce: types.EncodeNonce(g.Nonce), - Time: g.Timestamp, - ParentHash: g.ParentHash, - Extra: g.ExtraData, - GasLimit: g.GasLimit, - GasUsed: g.GasUsed, - BaseFee: g.BaseFee, - Difficulty: g.Difficulty, - MixDigest: g.Mixhash, - Coinbase: g.Coinbase, - Root: root, + Number: new(big.Int).SetUint64(g.Number), + Nonce: types.EncodeNonce(g.Nonce), + Time: g.Timestamp, + ParentHash: g.ParentHash, + Extra: g.ExtraData, + GasLimit: g.GasLimit, + GasUsed: g.GasUsed, + BaseFee: g.BaseFee, + Difficulty: g.Difficulty, + MixDigest: g.Mixhash, + Coinbase: g.Coinbase, + BlockAccessListHash: g.BlockAccessListHash, + Root: root, } if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go new file mode 100644 index 00000000000..0bdbf7ca467 --- /dev/null +++ b/core/parallel_state_processor.go @@ -0,0 +1,376 @@ +package core + +import ( + "cmp" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/core/vm" + "golang.org/x/sync/errgroup" + "slices" + "time" +) + +// ProcessResultWithMetrics wraps ProcessResult with some metrics that are +// emitted when executing blocks containing access lists. +type ProcessResultWithMetrics struct { + ProcessResult *ProcessResult + // the time it took to load modified prestate accounts from disk and instantiate statedbs for execution + PreProcessTime time.Duration + // the time it took to validate the block post transaction execution and state root calculation + PostProcessTime time.Duration + // the time it took to hash the state root, including intermediate node reads + RootCalcTime time.Duration + // the time that it took to load the prestate for accounts that were updated as part of + // the state root update + PrestateLoadTime time.Duration + // the time it took to execute all txs in the block + ExecTime time.Duration +} + +// ParallelStateProcessor is used to execute and verify blocks containing +// access lists. +type ParallelStateProcessor struct { + *StateProcessor + vmCfg *vm.Config +} + +// NewParallelStateProcessor returns a new ParallelStateProcessor instance. +func NewParallelStateProcessor(chain *HeaderChain, vmConfig *vm.Config) ParallelStateProcessor { + res := NewStateProcessor(chain) + return ParallelStateProcessor{ + res, + vmConfig, + } +} + +// called by resultHandler when all transactions have successfully executed. +// performs post-tx state transition (system contracts and withdrawals) +// and calculates the ProcessResult, returning it to be sent on resCh +// by resultHandler +func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateReads *bal.StateAccesses, tExecStart time.Time, postTxState *state.StateDB, receipts types.Receipts) *ProcessResultWithMetrics { + tExec := time.Since(tExecStart) + var requests [][]byte + tPostprocessStart := time.Now() + header := block.Header() + + balTracer, hooks := NewBlockAccessListTracer() + tracingStateDB := state.NewHookedState(postTxState, hooks) + context := NewEVMBlockContext(header, p.chain, nil) + postTxState.SetAccessListIndex(len(block.Transactions()) + 1) + + cfg := vm.Config{ + Tracer: hooks, + NoBaseFee: p.vmCfg.NoBaseFee, + EnablePreimageRecording: p.vmCfg.EnablePreimageRecording, + ExtraEips: slices.Clone(p.vmCfg.ExtraEips), + StatelessSelfValidation: p.vmCfg.StatelessSelfValidation, + EnableWitnessStats: p.vmCfg.EnableWitnessStats, + } + cfg.Tracer = hooks + evm := vm.NewEVM(context, tracingStateDB, p.chainConfig(), cfg) + + // 1. order the receipts by tx index + // 2. correctly calculate the cumulative gas used per receipt, returning bad block error if it goes over the allowed + slices.SortFunc(receipts, func(a, b *types.Receipt) int { + return cmp.Compare(a.TransactionIndex, b.TransactionIndex) + }) + + var cumulativeGasUsed uint64 + var allLogs []*types.Log + for _, receipt := range receipts { + receipt.CumulativeGasUsed = cumulativeGasUsed + receipt.GasUsed + cumulativeGasUsed += receipt.GasUsed + if receipt.CumulativeGasUsed > header.GasLimit { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: fmt.Errorf("gas limit exceeded")}, + } + } + allLogs = append(allLogs, receipt.Logs...) + } + + // Read requests if Prague is enabled. + if p.chainConfig().IsPrague(block.Number(), block.Time()) { + requests = [][]byte{} + // EIP-6110 + if err := ParseDepositLogs(&requests, allLogs, p.chainConfig()); err != nil { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: err}, + } + } + + // EIP-7002 + err := ProcessWithdrawalQueue(&requests, evm) + if err != nil { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: err}, + } + } + + // EIP-7251 + err = ProcessConsolidationQueue(&requests, evm) + if err != nil { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: err}, + } + } + } + + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) + p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body()) + // invoke FinaliseIdxChanges so that withdrawals are accounted for in the state diff + postTxState.Finalise(true) + + balTracer.OnBlockFinalization() + diff, stateReads := balTracer.builder.FinalizedIdxChanges() + allStateReads.Merge(stateReads) + + balIdx := len(block.Transactions()) + 1 + if err := postTxState.BlockAccessList().ValidateStateDiff(balIdx, diff); err != nil { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: err}, + } + } + + if err := postTxState.BlockAccessList().ValidateStateReads(*allStateReads); err != nil { + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{Error: err}, + } + } + + tPostprocess := time.Since(tPostprocessStart) + + return &ProcessResultWithMetrics{ + ProcessResult: &ProcessResult{ + Receipts: receipts, + Requests: requests, + Logs: allLogs, + GasUsed: cumulativeGasUsed, + }, + PostProcessTime: tPostprocess, + ExecTime: tExec, + } +} + +type txExecResult struct { + idx int // transaction index + receipt *types.Receipt + err error // non-EVM error which would render the block invalid + + stateReads bal.StateAccesses +} + +// resultHandler polls until all transactions have finished executing and the +// state root calculation is complete. The result is emitted on resCh. +func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxStateReads bal.StateAccesses, postTxState *state.StateDB, tExecStart time.Time, txResCh <-chan txExecResult, stateRootCalcResCh <-chan stateRootCalculationResult, resCh chan *ProcessResultWithMetrics) { + // 1. if the block has transactions, receive the execution results from all of them and return an error on resCh if any txs err'd + // 2. once all txs are executed, compute the post-tx state transition and produce the ProcessResult sending it on resCh (or an error if the post-tx state didn't match what is reported in the BAL) + var receipts []*types.Receipt + gp := new(GasPool) + gp.SetGas(block.GasLimit()) + var execErr error + var numTxComplete int + + allReads := make(bal.StateAccesses) + allReads.Merge(preTxStateReads) + if len(block.Transactions()) > 0 { + loop: + for { + select { + case res := <-txResCh: + if execErr == nil { + if res.err != nil { + execErr = res.err + } else { + if err := gp.SubGas(res.receipt.GasUsed); err != nil { + execErr = err + } else { + receipts = append(receipts, res.receipt) + allReads.Merge(res.stateReads) + } + } + } + numTxComplete++ + if numTxComplete == len(block.Transactions()) { + break loop + } + } + } + + if execErr != nil { + resCh <- &ProcessResultWithMetrics{ProcessResult: &ProcessResult{Error: execErr}} + return + } + } + + execResults := p.prepareExecResult(block, &allReads, tExecStart, postTxState, receipts) + rootCalcRes := <-stateRootCalcResCh + + if execResults.ProcessResult.Error != nil { + resCh <- execResults + } else if rootCalcRes.err != nil { + resCh <- &ProcessResultWithMetrics{ProcessResult: &ProcessResult{Error: rootCalcRes.err}} + } else { + execResults.RootCalcTime = rootCalcRes.rootCalcTime + execResults.PrestateLoadTime = rootCalcRes.prestateLoadTime + resCh <- execResults + } +} + +type stateRootCalculationResult struct { + err error + prestateLoadTime time.Duration + rootCalcTime time.Duration + root common.Hash +} + +// calcAndVerifyRoot performs the post-state root hash calculation, verifying +// it against what is reported by the block and returning a result on resCh. +func (p *ParallelStateProcessor) calcAndVerifyRoot(preState *state.StateDB, block *types.Block, resCh chan stateRootCalculationResult) { + // calculate and apply the block state modifications + root, prestateLoadTime, rootCalcTime := preState.BlockAccessList().StateRoot(preState) + + res := stateRootCalculationResult{ + root: root, + prestateLoadTime: prestateLoadTime, + rootCalcTime: rootCalcTime, + } + + if root != block.Root() { + res.err = fmt.Errorf("state root mismatch. local: %x. remote: %x", root, block.Root()) + } + resCh <- res +} + +// execTx executes single transaction returning a result which includes state accessed/modified +func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transaction, txIdx int, db *state.StateDB, signer types.Signer) *txExecResult { + header := block.Header() + balTracer, hooks := NewBlockAccessListTracer() + tracingStateDB := state.NewHookedState(db, hooks) + context := NewEVMBlockContext(header, p.chain, nil) + + cfg := vm.Config{ + Tracer: hooks, + NoBaseFee: p.vmCfg.NoBaseFee, + EnablePreimageRecording: p.vmCfg.EnablePreimageRecording, + ExtraEips: slices.Clone(p.vmCfg.ExtraEips), + StatelessSelfValidation: p.vmCfg.StatelessSelfValidation, + EnableWitnessStats: p.vmCfg.EnableWitnessStats, + } + cfg.Tracer = hooks + evm := vm.NewEVM(context, tracingStateDB, p.chainConfig(), cfg) + + msg, err := TransactionToMessage(tx, signer, header.BaseFee) + if err != nil { + err = fmt.Errorf("could not apply tx %d [%v]: %w", txIdx, tx.Hash().Hex(), err) + return &txExecResult{err: err} + } + gp := new(GasPool) + gp.SetGas(block.GasLimit()) + db.SetTxContext(tx.Hash(), txIdx) + var gasUsed uint64 + receipt, err := ApplyTransactionWithEVM(msg, gp, db, block.Number(), block.Hash(), context.Time, tx, &gasUsed, evm) + if err != nil { + err := fmt.Errorf("could not apply tx %d [%v]: %w", txIdx, tx.Hash().Hex(), err) + return &txExecResult{err: err} + } + + diff, accesses := balTracer.builder.FinalizedIdxChanges() + if err := db.BlockAccessList().ValidateStateDiff(txIdx+1, diff); err != nil { + return &txExecResult{err: err} + } + + return &txExecResult{ + idx: txIdx, + receipt: receipt, + stateReads: accesses, + } +} + +// Process performs EVM execution and state root computation for a block which is known +// to contain an access list. +func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResultWithMetrics, error) { + var ( + header = block.Header() + resCh = make(chan *ProcessResultWithMetrics) + signer = types.MakeSigner(p.chainConfig(), header.Number, header.Time) + ) + + txResCh := make(chan txExecResult) + pStart := time.Now() + var ( + tPreprocess time.Duration // time to create a set of prestates for parallel transaction execution + tExecStart time.Time + rootCalcResultCh = make(chan stateRootCalculationResult) + ) + + // Mutate the block and state according to any hard-fork specs + if p.chainConfig().DAOForkSupport && p.chainConfig().DAOForkBlock != nil && p.chainConfig().DAOForkBlock.Cmp(block.Number()) == 0 { + misc.ApplyDAOHardFork(statedb) + } + var ( + context vm.BlockContext + ) + alReader := state.NewBALReader(block, statedb) + statedb.SetBlockAccessList(alReader) + + balTracer, hooks := NewBlockAccessListTracer() + tracingStateDB := state.NewHookedState(statedb, hooks) + // TODO: figure out exactly why we need to set the hooks on the TracingStateDB and the vm.Config + cfg.Tracer = hooks + + context = NewEVMBlockContext(header, p.chain, nil) + evm := vm.NewEVM(context, tracingStateDB, p.chainConfig(), cfg) + + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + ProcessBeaconBlockRoot(*beaconRoot, evm) + } + if p.chainConfig().IsPrague(block.Number(), block.Time()) || p.chainConfig().IsVerkle(block.Number(), block.Time()) { + ProcessParentBlockHash(block.ParentHash(), evm) + } + + // TODO: weird that I have to manually call finalize here + balTracer.OnPreTxExecutionDone() + + diff, stateReads := balTracer.builder.FinalizedIdxChanges() + if err := statedb.BlockAccessList().ValidateStateDiff(0, diff); err != nil { + return nil, err + } + + // compute the post-tx state prestate (before applying final block system calls and eip-4895 withdrawals) + // the post-tx state transition is verified by resultHandler + postTxState := statedb.Copy() + + tPreprocess = time.Since(pStart) + + // execute transactions and state root calculation in parallel + + // TODO: figure out how to funnel the state reads from the bal tracer through to the post-block-exec state/slot read + // validation + tExecStart = time.Now() + go p.resultHandler(block, stateReads, postTxState, tExecStart, txResCh, rootCalcResultCh, resCh) + var workers errgroup.Group + startingState := statedb.Copy() + for i, tx := range block.Transactions() { + tx := tx + i := i + workers.Go(func() error { + res := p.execTx(block, tx, i, startingState.Copy(), signer) + txResCh <- *res + return nil + }) + } + + go p.calcAndVerifyRoot(statedb, block, rootCalcResultCh) + + res := <-resCh + if res.ProcessResult.Error != nil { + return nil, res.ProcessResult.Error + } + res.PreProcessTime = tPreprocess + // res.PreProcessLoadTime = tPreprocessLoad + return res, nil +} diff --git a/core/state/bal_reader.go b/core/state/bal_reader.go new file mode 100644 index 00000000000..3bd343abf7a --- /dev/null +++ b/core/state/bal_reader.go @@ -0,0 +1,376 @@ +package state + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" + "sync" + "time" +) + +// TODO: probably unnecessary to cache the resolved state object here as it will already be in the db cache? +// ^ experiment with the performance of keeping this as-is vs just using the db cache. +type prestateResolver struct { + inProgress map[common.Address]chan struct{} + resolved sync.Map + ctx context.Context + cancel func() +} + +func (p *prestateResolver) resolve(r Reader, addrs []common.Address) { + p.inProgress = make(map[common.Address]chan struct{}) + p.ctx, p.cancel = context.WithCancel(context.Background()) + + for _, addr := range addrs { + p.inProgress[addr] = make(chan struct{}) + } + + for _, addr := range addrs { + resolveAddr := addr + go func() { + select { + case <-p.ctx.Done(): + return + default: + } + + acct, err := r.Account(resolveAddr) + if err != nil { + // TODO: what do here? + } + p.resolved.Store(resolveAddr, acct) + close(p.inProgress[resolveAddr]) + }() + } +} + +func (p *prestateResolver) stop() { + p.cancel() +} + +func (p *prestateResolver) account(addr common.Address) *types.StateAccount { + if _, ok := p.inProgress[addr]; !ok { + return nil + } + + select { + case <-p.inProgress[addr]: + } + res, exist := p.resolved.Load(addr) + if !exist { + return nil + } + return res.(*types.StateAccount) +} + +func (r *BALReader) initObjFromDiff(db *StateDB, addr common.Address, a *types.StateAccount, diff *bal.AccountMutations) *stateObject { + var acct *types.StateAccount + if a == nil { + acct = &types.StateAccount{ + Nonce: 0, + Balance: uint256.NewInt(0), + Root: types.EmptyRootHash, + CodeHash: types.EmptyCodeHash[:], + } + } else { + acct = a.Copy() + } + if diff == nil { + return newObject(db, addr, acct) + } + + if diff.Nonce != nil { + acct.Nonce = *diff.Nonce + } + if diff.Balance != nil { + acct.Balance = new(uint256.Int).Set(diff.Balance) + } + obj := newObject(db, addr, acct) + if diff.Code != nil { + obj.setCode(crypto.Keccak256Hash(diff.Code), diff.Code) + } + if diff.StorageWrites != nil { + for key, val := range diff.StorageWrites { + obj.pendingStorage[key] = val + } + } + if obj.empty() { + return nil + } + return obj +} + +func (s *BALReader) initMutatedObjFromDiff(db *StateDB, addr common.Address, a *types.StateAccount, diff *bal.AccountMutations) *stateObject { + var acct *types.StateAccount + if a == nil { + acct = &types.StateAccount{ + Nonce: 0, + Balance: uint256.NewInt(0), + Root: types.EmptyRootHash, + CodeHash: types.EmptyCodeHash[:], + } + } else { + acct = a.Copy() + } + obj := newObject(db, addr, acct) + if diff.Nonce != nil { + obj.SetNonce(*diff.Nonce) + } + if diff.Balance != nil { + obj.SetBalance(new(uint256.Int).Set(diff.Balance)) + } + if diff.Code != nil { + obj.SetCode(crypto.Keccak256Hash(diff.Code), diff.Code) + } + if diff.StorageWrites != nil { + for key, val := range diff.StorageWrites { + obj.SetState(key, val) + } + } + return obj +} + +// BALReader provides methods for reading account state from a block access +// list. State values returned from the Reader methods must not be modified. +type BALReader struct { + block *types.Block + accesses map[common.Address]*bal.AccountAccess + prestateReader prestateResolver +} + +// NewBALReader constructs a new reader from an access list. db is expected to have been instantiated with a reader. +func NewBALReader(block *types.Block, db *StateDB) *BALReader { + r := &BALReader{accesses: make(map[common.Address]*bal.AccountAccess), block: block} + for _, acctDiff := range *block.Body().AccessList { + r.accesses[acctDiff.Address] = &acctDiff + } + r.prestateReader.resolve(db.Reader(), r.ModifiedAccounts()) + return r +} + +// ModifiedAccounts returns a list of all accounts with mutations in the access list +func (r *BALReader) ModifiedAccounts() (res []common.Address) { + for addr, access := range r.accesses { + if len(access.NonceChanges) != 0 || len(access.CodeChanges) != 0 || len(access.StorageChanges) != 0 || len(access.BalanceChanges) != 0 { + res = append(res, addr) + } + } + return res +} + +func (r *BALReader) ValidateStateReads(allReads bal.StateAccesses) error { + // 1. remove any slots from 'allReads' which were written + // 2. validate that the read set in the BAL matches 'allReads' exactly + for addr, reads := range allReads { + balAcctDiff := r.readAccountDiff(addr, len(r.block.Transactions())+2) + if balAcctDiff != nil { + for writeSlot := range balAcctDiff.StorageWrites { + delete(reads, writeSlot) + } + } + if _, ok := r.accesses[addr]; !ok { + return fmt.Errorf("%x wasn't in BAL", addr) + } + + expectedReads := r.accesses[addr].StorageReads + if len(reads) != len(expectedReads) { + return fmt.Errorf("mismatch between the number of computed reads and number of expected reads") + } + + for _, slot := range expectedReads { + if _, ok := reads[slot]; !ok { + return fmt.Errorf("expected read is missing from BAL") + } + } + } + + // TODO: where do we validate that the storage read/write sets are distinct? + + return nil +} + +func (r *BALReader) AccessedState() (res map[common.Address]map[common.Hash]struct{}) { + res = make(map[common.Address]map[common.Hash]struct{}) + for addr, accesses := range r.accesses { + if len(accesses.StorageReads) > 0 { + res[addr] = make(map[common.Hash]struct{}) + for _, slot := range accesses.StorageReads { + res[addr][slot] = struct{}{} + } + } else if len(accesses.BalanceChanges) == 0 && len(accesses.NonceChanges) == 0 && len(accesses.StorageChanges) == 0 && len(accesses.CodeChanges) == 0 { + res[addr] = make(map[common.Hash]struct{}) + } + } + return +} + +// TODO: it feels weird that this modifies the prestate instance. However, it's needed because it will +// subsequently be used in Commit. +func (r *BALReader) StateRoot(prestate *StateDB) (root common.Hash, prestateLoadTime time.Duration, rootUpdateTime time.Duration) { + lastIdx := len(r.block.Transactions()) + 1 + modifiedAccts := r.ModifiedAccounts() + startPrestateLoad := time.Now() + for _, addr := range modifiedAccts { + diff := r.readAccountDiff(addr, lastIdx) + acct := r.prestateReader.account(addr) + obj := r.initMutatedObjFromDiff(prestate, addr, acct, diff) + if obj != nil { + prestate.setStateObject(obj) + } + } + prestateLoadTime = time.Since(startPrestateLoad) + rootUpdateStart := time.Now() + root = prestate.IntermediateRoot(true) + rootUpdateTime = time.Since(rootUpdateStart) + return root, prestateLoadTime, rootUpdateTime +} + +// changesAt returns all state changes at the given index. +func (r *BALReader) changesAt(idx int) *bal.StateDiff { + res := &bal.StateDiff{make(map[common.Address]*bal.AccountMutations)} + for addr, _ := range r.accesses { + accountChanges := r.accountChangesAt(addr, idx) + if accountChanges != nil { + res.Mutations[addr] = accountChanges + } + } + return res +} + +// accountChangesAt returns the state changes of an account at a given index, +// or nil if there are no changes. +func (r *BALReader) accountChangesAt(addr common.Address, idx int) *bal.AccountMutations { + acct, exist := r.accesses[addr] + if !exist { + return nil + } + + var res bal.AccountMutations + + for i := len(acct.BalanceChanges) - 1; i >= 0; i-- { + if acct.BalanceChanges[i].TxIdx == uint16(idx) { + res.Balance = acct.BalanceChanges[i].Balance + } + if acct.BalanceChanges[i].TxIdx < uint16(idx) { + break + } + } + + for i := len(acct.CodeChanges) - 1; i >= 0; i-- { + if acct.CodeChanges[i].TxIdx == uint16(idx) { + res.Code = acct.CodeChanges[i].Code + break + } + if acct.CodeChanges[i].TxIdx < uint16(idx) { + break + } + } + + for i := len(acct.NonceChanges) - 1; i >= 0; i-- { + if acct.NonceChanges[i].TxIdx == uint16(idx) { + res.Nonce = &acct.NonceChanges[i].Nonce + break + } + if acct.NonceChanges[i].TxIdx < uint16(idx) { + break + } + } + + for i := len(acct.StorageChanges) - 1; i >= 0; i-- { + if res.StorageWrites == nil { + res.StorageWrites = make(map[common.Hash]common.Hash) + } + slotWrites := acct.StorageChanges[i] + + for j := len(slotWrites.Accesses) - 1; j >= 0; j-- { + if slotWrites.Accesses[j].TxIdx == uint16(idx) { + res.StorageWrites[slotWrites.Slot] = slotWrites.Accesses[j].ValueAfter + break + } + if slotWrites.Accesses[j].TxIdx < uint16(idx) { + break + } + } + if len(res.StorageWrites) == 0 { + res.StorageWrites = nil + } + } + + if res.Code == nil && res.Nonce == nil && len(res.StorageWrites) == 0 && res.Balance == nil { + return nil + } + return &res +} + +func (r *BALReader) isModified(addr common.Address) bool { + access, ok := r.accesses[addr] + if !ok { + return false + } + return len(access.StorageChanges) > 0 || len(access.BalanceChanges) > 0 || len(access.CodeChanges) > 0 || len(access.NonceChanges) > 0 +} + +func (r *BALReader) readAccount(db *StateDB, addr common.Address, idx int) *stateObject { + diff := r.readAccountDiff(addr, idx) + prestate := r.prestateReader.account(addr) + return r.initObjFromDiff(db, addr, prestate, diff) +} + +// readAccountDiff returns the accumulated state changes of an account up through idx. +func (r *BALReader) readAccountDiff(addr common.Address, idx int) *bal.AccountMutations { + diff, exist := r.accesses[addr] + if !exist { + return nil + } + + var res bal.AccountMutations + + for i := 0; i < len(diff.BalanceChanges) && diff.BalanceChanges[i].TxIdx <= uint16(idx); i++ { + res.Balance = diff.BalanceChanges[i].Balance + } + + for i := 0; i < len(diff.CodeChanges) && diff.CodeChanges[i].TxIdx <= uint16(idx); i++ { + res.Code = diff.CodeChanges[i].Code + } + + for i := 0; i < len(diff.NonceChanges) && diff.NonceChanges[i].TxIdx <= uint16(idx); i++ { + res.Nonce = &diff.NonceChanges[i].Nonce + } + + if len(diff.StorageChanges) > 0 { + res.StorageWrites = make(map[common.Hash]common.Hash) + for _, slotWrites := range diff.StorageChanges { + for i := 0; i < len(slotWrites.Accesses) && slotWrites.Accesses[i].TxIdx <= uint16(idx); i++ { + res.StorageWrites[slotWrites.Slot] = slotWrites.Accesses[i].ValueAfter + } + } + } + + return &res +} + +// ValidateStateDiff returns an error if the computed state diff is not equal to +// diff reported from the access list at the given index. +func (r *BALReader) ValidateStateDiff(idx int, computedDiff *bal.StateDiff) error { + balChanges := r.changesAt(idx) + for addr, state := range balChanges.Mutations { + computedAccountDiff, ok := computedDiff.Mutations[addr] + if !ok { + return fmt.Errorf("BAL contained account %x which wasn't present in computed state diff", addr) + } + + if !state.Eq(computedAccountDiff) { + return fmt.Errorf("difference between computed state diff and BAL entry for account %x", addr) + } + } + + if len(balChanges.Mutations) != len(computedDiff.Mutations) { + return fmt.Errorf("computed state diff contained mutated accounts which weren't reported in BAL") + } + + return nil +} diff --git a/core/state/database.go b/core/state/database.go index 58d0ccfe829..f912caeaf59 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -99,12 +99,18 @@ type Trie interface { // in the trie with provided address. UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error + // UpdateAccountBatch attempts to update a list accounts in the batch manner. + UpdateAccountBatch(addresses []common.Address, accounts []*types.StateAccount, _ []int) error + // UpdateStorage associates key with value in the trie. If value has length zero, // any existing value is deleted from the trie. The value bytes must not be modified // by the caller while they are stored in the trie. If a node was not found in the // database, a trie.MissingNodeError is returned. UpdateStorage(addr common.Address, key, value []byte) error + // UpdateStorageBatch attempts to update a list storages in the batch manner. + UpdateStorageBatch(_ common.Address, keys [][]byte, values [][]byte) error + // DeleteAccount abstracts an account deletion from the trie. DeleteAccount(address common.Address) error diff --git a/core/state/journal.go b/core/state/journal.go index f3f976f24fe..b95d7e90fbc 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -381,7 +381,7 @@ func (ch nonceChange) copy() journalEntry { } func (ch codeChange) revert(s *StateDB) { - s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode) + s.getStateObject(ch.account).setCodeModified(crypto.Keccak256Hash(ch.prevCode), ch.prevCode) } func (ch codeChange) dirtied() *common.Address { diff --git a/core/state/state_object.go b/core/state/state_object.go index fdeb4254c1b..ed1ea72964e 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -83,6 +83,8 @@ type stateObject struct { // the contract is just created within the current transaction, or when the // object was previously existent and is being deployed as a contract within // the current transaction. + // + // the flag is set upon beginning of contract initcode execution, not when the code is actually deployed to the address. newContract bool } @@ -176,6 +178,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { if value, pending := s.pendingStorage[key]; pending { return value } + if value, cached := s.originStorage[key]; cached { return value } @@ -244,9 +247,10 @@ func (s *stateObject) finalise() { // The slot is reverted to its original value, delete the entry // to avoid thrashing the data structures. delete(s.uncommittedStorage, key) + } else if exist { // The slot is modified to another value and the slot has been - // tracked for commit, do nothing here. + // tracked for commit in uncommittedStorage. } else { // The slot is different from its original value and hasn't been // tracked for commit yet. @@ -323,8 +327,10 @@ func (s *stateObject) updateTrie() (Trie, error) { // into a shortnode. This requires `B` to be resolved from disk. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. var ( - deletions []common.Hash - used = make([]common.Hash, 0, len(s.uncommittedStorage)) + deletions []common.Hash + used = make([]common.Hash, 0, len(s.uncommittedStorage)) + updateKeys [][]byte + updateValues [][]byte ) for key, origin := range s.uncommittedStorage { // Skip noop changes, persist actual changes @@ -338,10 +344,8 @@ func (s *stateObject) updateTrie() (Trie, error) { continue } if (value != common.Hash{}) { - if err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])); err != nil { - s.db.setError(err) - return nil, err - } + updateKeys = append(updateKeys, key[:]) + updateValues = append(updateValues, common.TrimLeftZeroes(value[:])) s.db.StorageUpdated.Add(1) } else { deletions = append(deletions, key) @@ -349,6 +353,12 @@ func (s *stateObject) updateTrie() (Trie, error) { // Cache the items for preloading used = append(used, key) // Copy needed for closure } + if len(updateKeys) > 0 { + if err := tr.UpdateStorageBatch(common.Address{}, updateKeys, updateValues); err != nil { + s.db.setError(err) + return nil, err + } + } for _, key := range deletions { if err := tr.DeleteStorage(s.address, key[:]); err != nil { s.db.setError(err) @@ -564,13 +574,18 @@ func (s *stateObject) CodeSize() int { func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) { prev = slices.Clone(s.code) s.db.journal.setCode(s.address, prev) - s.setCode(codeHash, code) + s.setCodeModified(codeHash, code) return prev } func (s *stateObject) setCode(codeHash common.Hash, code []byte) { s.code = code s.data.CodeHash = codeHash[:] +} + +// setCodeModified sets the code and hash and dirty markers. +func (s *stateObject) setCodeModified(codeHash common.Hash, code []byte) { + s.setCode(codeHash, code) s.dirtyCode = true } diff --git a/core/state/statedb.go b/core/state/statedb.go index b770698255e..429a24d848b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -118,6 +118,14 @@ type StateDB struct { // The tx context and all occurred logs in the scope of transaction. thash common.Hash txIndex int + sender common.Address + + // block access list modifications will be recorded with this index. + // 0 - state access before transaction execution + // 1 -> len(block txs) - state access of each transaction + // len(block txs) + 1 - state access after transaction execution. + balIndex int + logs map[common.Hash][]*types.Log logSize uint @@ -139,6 +147,8 @@ type StateDB struct { witness *stateless.Witness witnessStats *stateless.WitnessStats + blockAccessList *BALReader + // Measurements gathered during execution for debugging purposes AccountReads time.Duration AccountHashes time.Duration @@ -158,6 +168,10 @@ type StateDB struct { StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition } +func (s *StateDB) BlockAccessList() *BALReader { + return s.blockAccessList +} + // New creates a new state from a given trie. func New(root common.Hash, db Database) (*StateDB, error) { reader, err := db.Reader(root) @@ -285,6 +299,38 @@ func (s *StateDB) AddRefund(gas uint64) { s.refund += gas } +func (s *StateDB) SetBlockAccessList(al *BALReader) { + s.blockAccessList = al +} + +// LoadModifiedPrestate instantiates the live object based on accounts +// which appeared in the total state diff of a block, and were also preexisting. +func (s *StateDB) LoadModifiedPrestate(addrs []common.Address) (res map[common.Address]*types.StateAccount) { + stateAccounts := new(sync.Map) + wg := new(sync.WaitGroup) + res = make(map[common.Address]*types.StateAccount) + + for _, addr := range addrs { + wg.Add(1) + go func(addr common.Address) { + acct, err := s.reader.Account(addr) + if err == nil && acct != nil { // TODO: what should we do if the error is not nil? + stateAccounts.Store(addr, acct) + } + wg.Done() + }(addr) + } + wg.Wait() + stateAccounts.Range(func(addr any, val any) bool { + address := addr.(common.Address) + stateAccount := val.(*types.StateAccount) + res[address] = stateAccount + return true + }) + + return res +} + // SubRefund removes gas from the refund counter. // This method will panic if the refund counter goes below zero func (s *StateDB) SubRefund(gas uint64) { @@ -301,6 +347,11 @@ func (s *StateDB) Exist(addr common.Address) bool { return s.getStateObject(addr) != nil } +func (s *StateDB) ExistBeforeCurTx(addr common.Address) bool { + obj := s.getStateObject(addr) + return obj != nil && !obj.newContract +} + // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) func (s *StateDB) Empty(addr common.Address) bool { @@ -576,6 +627,25 @@ func (s *StateDB) updateStateObject(obj *stateObject) { s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) } } +func (s *StateDB) updateStateObjects(objs []*stateObject) { + var addrs []common.Address + var accts []*types.StateAccount + + for _, obj := range objs { + addrs = append(addrs, obj.Address()) + accts = append(accts, &obj.data) + } + + if err := s.trie.UpdateAccountBatch(addrs, accts, nil); err != nil { + s.setError(fmt.Errorf("updateStateObjects error: %v", err)) + } + + for _, obj := range objs { + if obj.dirtyCode { + s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) + } + } +} // deleteStateObject removes the given object from the state trie. func (s *StateDB) deleteStateObject(addr common.Address) { @@ -595,6 +665,24 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { if _, ok := s.stateObjectsDestruct[addr]; ok { return nil } + + // if we are executing against a block access list, construct the account + // state at the current tx index by applying the access-list diff on top + // of the prestate value for the account. + if s.blockAccessList != nil && s.balIndex != 0 && s.blockAccessList.isModified(addr) { + acct := s.blockAccessList.readAccount(s, addr, s.balIndex-1) + if acct != nil { + s.setStateObject(acct) + return acct + } + return nil + + // if the acct was nil, it might be non-existent or was not explicitly requested for loading from the blockAcccessList object. + // try to load it from the snapshot. + + // TODO: if the acct was non-existent because it was deleted, we should just return nil herre. + } + s.AccountLoaded++ start := time.Now() @@ -631,6 +719,7 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { if obj == nil { obj = s.createObject(addr) } + return obj } @@ -679,10 +768,14 @@ func (s *StateDB) Copy() *StateDB { refund: s.refund, thash: s.thash, txIndex: s.txIndex, + balIndex: s.txIndex, logs: make(map[common.Hash][]*types.Log, len(s.logs)), logSize: s.logSize, preimages: maps.Clone(s.preimages), + // don't deep-copy these + blockAccessList: s.blockAccessList, + // Do we need to copy the access list and transient storage? // In practice: No. At the start of a transaction, these two lists are empty. // In practice, we only ever copy state _between_ transactions/blocks, never @@ -744,6 +837,9 @@ func (s *StateDB) GetRefund() uint64 { // Finalise finalises the state by removing the destructed objects and clears // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. +// +// If EnableStateDiffRecording has been called, it returns a state diff containing +// the state which was mutated since the previous invocation of Finalise. Otherwise, nil. func (s *StateDB) Finalise(deleteEmptyObjects bool) { addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties)) for addr := range s.journal.dirties { @@ -769,8 +865,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { } else { obj.finalise() s.markUpdate(addr) - } - // At this point, also ship the address off to the precacher. The precacher + } // At this point, also ship the address off to the precacher. The precacher // will start loading tries, and when the change is eventually committed, // the commit-phase will be a lot faster addressesToPrefetch = append(addressesToPrefetch, addr) // Copy needed for closure @@ -780,6 +875,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err) } } + // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() } @@ -788,7 +884,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // It is called in between transactions to get the root hash that // goes into transaction receipts. func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { - // Finalise all the dirty storage states and write them into the tries + // FinaliseIdxChanges all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) // Initialize the trie if it's not constructed yet. If the prefetch @@ -929,6 +1025,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { var ( usedAddrs []common.Address deletedAddrs []common.Address + updatedObjs []*stateObject ) for addr, op := range s.mutations { if op.applied { @@ -939,11 +1036,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if op.isDelete() { deletedAddrs = append(deletedAddrs, addr) } else { - s.updateStateObject(s.stateObjects[addr]) + updatedObjs = append(updatedObjs, s.stateObjects[addr]) s.AccountUpdated += 1 } usedAddrs = append(usedAddrs, addr) // Copy needed for closure } + if len(updatedObjs) > 0 { + s.updateStateObjects(updatedObjs) + } for _, deletedAddr := range deletedAddrs { s.deleteStateObject(deletedAddr) s.AccountDeleted += 1 @@ -955,9 +1055,21 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } // Track the amount of time wasted on hashing the account trie defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) - hash := s.trie.Hash() - + /* + it, err := s.trie.NodeIterator([]byte{}) + if err != nil { + panic(err) + } + fmt.Println("state trie") + for it.Next(true) { + if it.Leaf() { + fmt.Printf("%x: %x\n", it.Path(), it.LeafBlob()) + } else { + fmt.Printf("%x: %x\n", it.Path(), it.Hash()) + } + } + */ // If witness building is enabled, gather the account trie witness if s.witness != nil { witness := s.trie.Witness() @@ -966,6 +1078,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.witnessStats.Add(witness, common.Hash{}) } } + return hash } @@ -975,6 +1088,19 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { func (s *StateDB) SetTxContext(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti + s.balIndex = ti + 1 +} + +// SetAccessListIndex sets the current index that state mutations will +// be reported as in the BAL. It is only relevant if this StateDB instance +// is being used in the BAL construction path. +func (s *StateDB) SetAccessListIndex(idx int) { + s.balIndex = idx +} + +// SetTxSender sets the sender of the currently-executing transaction. +func (s *StateDB) SetTxSender(sender common.Address) { + s.sender = sender } func (s *StateDB) clearJournalAndRefund() { @@ -1160,6 +1286,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum if s.dbErr != nil { return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } + // Finalize any pending changes and merge everything into the tries s.IntermediateRoot(deleteEmptyObjects) diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index d2595bcefed..bf80a36a13d 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -17,8 +17,6 @@ package state import ( - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" @@ -27,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" + "math/big" ) // hookedStateDB represents a statedb which emits calls to tracing-hooks @@ -54,22 +53,37 @@ func (s *hookedStateDB) CreateContract(addr common.Address) { } func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetBalance(addr) } func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetNonce(addr) } func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetCodeHash(addr) } func (s *hookedStateDB) GetCode(addr common.Address) []byte { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetCode(addr) } func (s *hookedStateDB) GetCodeSize(addr common.Address) int { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetCodeSize(addr) } @@ -86,14 +100,23 @@ func (s *hookedStateDB) GetRefund() uint64 { } func (s *hookedStateDB) GetStateAndCommittedState(addr common.Address, hash common.Hash) (common.Hash, common.Hash) { + if s.hooks.OnStorageRead != nil { + s.hooks.OnStorageRead(addr, hash) + } return s.inner.GetStateAndCommittedState(addr, hash) } func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + if s.hooks.OnStorageRead != nil { + s.hooks.OnStorageRead(addr, hash) + } return s.inner.GetState(addr, hash) } func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.GetStorageRoot(addr) } @@ -106,14 +129,23 @@ func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common } func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.HasSelfDestructed(addr) } func (s *hookedStateDB) Exist(addr common.Address) bool { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.Exist(addr) } func (s *hookedStateDB) Empty(addr common.Address) bool { + if s.hooks.OnAccountRead != nil { + s.hooks.OnAccountRead(addr) + } return s.inner.Empty(addr) } @@ -216,57 +248,25 @@ func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value } func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { - var prevCode []byte - var prevCodeHash common.Hash - - if s.hooks.OnCodeChange != nil { - prevCode = s.inner.GetCode(address) - prevCodeHash = s.inner.GetCodeHash(address) - } - prev := s.inner.SelfDestruct(address) if s.hooks.OnBalanceChange != nil && !prev.IsZero() { s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) } - if len(prevCode) > 0 { - if s.hooks.OnCodeChangeV2 != nil { - s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct) - } else if s.hooks.OnCodeChange != nil { - s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) - } - } - return prev } -func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) { - var prevCode []byte - var prevCodeHash common.Hash - - if s.hooks.OnCodeChange != nil { - prevCodeHash = s.inner.GetCodeHash(address) - prevCode = s.inner.GetCode(address) - } - - prev, changed := s.inner.SelfDestruct6780(address) - - if s.hooks.OnBalanceChange != nil && !prev.IsZero() { - s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) - } - - if changed && len(prevCode) > 0 { - if s.hooks.OnCodeChangeV2 != nil { - s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct) - } else if s.hooks.OnCodeChange != nil { - s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) - } - } +func (s *hookedStateDB) SelfDestruct6780(src common.Address) (uint256.Int, bool) { + prev, changed := s.inner.SelfDestruct6780(src) return prev, changed } +func (s *hookedStateDB) ExistBeforeCurTx(addr common.Address) bool { + return s.inner.ExistBeforeCurTx(addr) +} + func (s *hookedStateDB) AddLog(log *types.Log) { // The inner will modify the log (add fields), so invoke that first s.inner.AddLog(log) @@ -277,16 +277,42 @@ func (s *hookedStateDB) AddLog(log *types.Log) { func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { defer s.inner.Finalise(deleteEmptyObjects) - if s.hooks.OnBalanceChange == nil { - return - } - for addr := range s.inner.journal.dirties { - obj := s.inner.stateObjects[addr] - if obj != nil && obj.selfDestructed { - // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); bal.Sign() != 0 { - s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + if s.hooks.OnSelfDestructChange != nil || s.hooks.OnBalanceChange != nil || s.hooks.OnNonceChangeV2 != nil || s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil { + for addr := range s.inner.journal.dirties { + obj := s.inner.stateObjects[addr] + if obj != nil && obj.selfDestructed { + if obj.selfDestructed && s.hooks.OnSelfDestructChange != nil { + // when executing, can we tell the difference between + s.hooks.OnSelfDestructChange(obj.address) + } + + // If ether was sent to account post-selfdestruct it is burnt. + if s.hooks.OnBalanceChange != nil { + if bal := obj.Balance(); bal.Sign() != 0 { + s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + } + } + if s.hooks.OnNonceChangeV2 != nil { + prevNonce := obj.Nonce() + s.hooks.OnNonceChangeV2(addr, prevNonce, 0, tracing.NonceChangeSelfdestruct) + } + prevCodeHash := s.inner.GetCodeHash(addr) + prevCode := s.inner.GetCode(addr) + + // if an initcode invokes selfdestruct, do not emit a code change. + if prevCodeHash == types.EmptyCodeHash { + continue + } + if s.hooks.OnCodeChangeV2 != nil { + s.hooks.OnCodeChangeV2(addr, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct) + } else if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(addr, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + } } } } } + +func (s *hookedStateDB) TxIndex() int { + return s.inner.TxIndex() +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 661d17bb7be..d1d4c59d1e8 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -175,10 +175,10 @@ func TestCopy(t *testing.T) { orig.Finalise(false) // Copy the state - copy := orig.Copy() + copy := orig.Copy().(*StateDB) // Copy the copy state - ccopy := copy.Copy() + ccopy := copy.Copy().(*StateDB) // modify all in memory for i := byte(0); i < 255; i++ { @@ -191,7 +191,7 @@ func TestCopy(t *testing.T) { ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i))) } - // Finalise the changes on all concurrently + // FinaliseIdxChanges the changes on all concurrently finalise := func(wg *sync.WaitGroup, db *StateDB) { defer wg.Done() db.Finalise(true) @@ -243,7 +243,7 @@ func TestCopyWithDirtyJournal(t *testing.T) { amount := uint256.NewInt(uint64(i)) obj.SetBalance(new(uint256.Int).Sub(obj.Balance(), amount)) } - cpy := orig.Copy() + cpy := orig.Copy().(*StateDB) orig.Finalise(true) for i := byte(0); i < 255; i++ { @@ -278,7 +278,7 @@ func TestCopyObjectState(t *testing.T) { obj.data.Root = common.HexToHash("0xdeadbeef") } orig.Finalise(true) - cpy := orig.Copy() + cpy := orig.Copy().(*StateDB) for _, op := range cpy.mutations { if have, want := op.applied, false; have != want { t.Fatalf("Error in test itself, the 'done' flag should not be set before Commit, have %v want %v", have, want) @@ -528,7 +528,7 @@ func (test *snapshotTest) run() bool { for i, action := range test.actions { if len(test.snapshots) > sindex && i == test.snapshots[sindex] { snapshotRevs[sindex] = state.Snapshot() - checkstates[sindex] = state.Copy() + checkstates[sindex] = state.Copy().(*StateDB) sindex++ } action.fn(action, state) @@ -747,7 +747,7 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the non-committed state database and check pre/post commit balance - copyOne := state.Copy() + copyOne := state.Copy().(*StateDB) if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42) } @@ -761,7 +761,7 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the copy and check the balance once more - copyTwo := copyOne.Copy() + copyTwo := copyOne.Copy().(*StateDB) if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42) } @@ -820,7 +820,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the non-committed state database and check pre/post commit balance - copyOne := state.Copy() + copyOne := state.Copy().(*StateDB) if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42) } @@ -834,7 +834,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the copy and check the balance once more - copyTwo := copyOne.Copy() + copyTwo := copyOne.Copy().(*StateDB) if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42) } @@ -848,7 +848,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the copy-copy and check the balance once more - copyThree := copyTwo.Copy() + copyThree := copyTwo.Copy().(*StateDB) if balance := copyThree.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42) } @@ -896,7 +896,7 @@ func TestCommitCopy(t *testing.T) { state.Commit(1, true, false) // Copy the committed state database, the copied one is not fully functional. - copied := state.Copy() + copied := state.Copy().(*StateDB) if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("unexpected balance: have %v", balance) } @@ -1098,7 +1098,7 @@ func TestStateDBAccessList(t *testing.T) { verifySlots("bb", "01", "02") // Make a copy - stateCopy1 := state.Copy() + stateCopy1 := state.Copy().(*StateDB) if exp, got := 4, state.journal.length(); exp != got { t.Fatalf("journal length mismatch: have %d, want %d", got, exp) } diff --git a/core/state_processor.go b/core/state_processor.go index b66046f5017..e7ade93c7a2 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -79,7 +79,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ) // Apply pre-execution system calls. - var tracingStateDB = vm.StateDB(statedb) + var tracingStateDB vm.StateDB = statedb if hooks := cfg.Tracer; hooks != nil { tracingStateDB = state.NewHookedState(statedb, hooks) } @@ -93,6 +93,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ProcessParentBlockHash(block.ParentHash(), evm) } + if hooks := cfg.Tracer; hooks != nil { + hooks.OnPreTxExecutionDone() + } + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -108,6 +112,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } + + // TODO: how do we signal to the BAL tracer that we are computing post-tx state changes here? + // if there are no txs in the block, then it will just record these state diffs at idx 0 + // Read requests if Prague is enabled. var requests [][]byte if config.IsPrague(block.Number(), block.Time()) { @@ -118,17 +126,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // EIP-7002 if err := ProcessWithdrawalQueue(&requests, evm); err != nil { - return nil, fmt.Errorf("failed to process withdrawal queue: %w", err) + return nil, err } // EIP-7251 if err := ProcessConsolidationQueue(&requests, evm); err != nil { - return nil, fmt.Errorf("failed to process consolidation queue: %w", err) + return nil, err } } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body()) + if hooks := cfg.Tracer; hooks != nil { + hooks.OnBlockFinalization() + } + return &ProcessResult{ Receipts: receipts, Requests: requests, @@ -154,6 +166,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, if err != nil { return nil, err } + // Update the state with pending changes. var root []byte if evm.ChainConfig().IsByzantium(blockNumber) { @@ -213,7 +226,8 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header * return nil, err } // Create a new context to be used in the EVM environment - return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm) + receipts, err := ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm) + return receipts, err } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root diff --git a/core/state_transition.go b/core/state_transition.go index bf5ac07636d..622d66806d6 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -19,9 +19,6 @@ package core import ( "bytes" "fmt" - "math" - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -29,6 +26,8 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + "math" + "math/big" ) // ExecutionResult includes all output after executing given evm @@ -617,16 +616,22 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas) } + prevDelegation, isDelegated := types.ParseDelegation(st.state.GetCode(authority)) + // Update nonce and account code. st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization) if auth.Address == (common.Address{}) { // Delegation to zero address means clear. - st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear) + if isDelegated { + st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear) + } return nil } - // Otherwise install delegation to auth.Address. - st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization) + // install delegation to auth.Address if the delegation changed + if !isDelegated || auth.Address != prevDelegation { + st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization) + } return nil } diff --git a/core/stateless.go b/core/stateless.go index b20c909da67..6067546387d 100644 --- a/core/stateless.go +++ b/core/stateless.go @@ -70,7 +70,7 @@ func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *typ if err != nil { return common.Hash{}, common.Hash{}, err } - if err = validator.ValidateState(block, db, res, true); err != nil { + if err = validator.ValidateState(block, db, res, true, true); err != nil { return common.Hash{}, common.Hash{}, err } // Almost everything validated, but receipt and state root needs to be returned diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 8e50dc3d8f3..789436c5b83 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -127,7 +127,6 @@ type ( CloseHook = func() // BlockStartHook is called before executing `block`. - // `td` is the total difficulty prior to `block`. BlockStartHook = func(event BlockEvent) // BlockEndHook is called after executing a block. @@ -141,24 +140,25 @@ type ( // GenesisBlockHook is called when the genesis block is being processed. GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) - // OnSystemCallStartHook is called when a system call is about to be executed. Today, - // this hook is invoked when the EIP-4788 system call is about to be executed to set the - // beacon block root. + // OnSystemCallStartHook is called when a system call is about to be executed. + // Today, this hook is invoked when the EIP-4788 system call is about to be + // executed to set the beacon block root. // - // After this hook, the EVM call tracing will happened as usual so you will receive a `OnEnter/OnExit` - // as well as state hooks between this hook and the `OnSystemCallEndHook`. + // After this hook, the EVM call tracing will happened as usual so you will + // receive a `OnEnter/OnExit` as well as state hooks between this hook and + // the `OnSystemCallEndHook`. // - // Note that system call happens outside normal transaction execution, so the `OnTxStart/OnTxEnd` hooks - // will not be invoked. + // Note that system call happens outside normal transaction execution, so + // the `OnTxStart/OnTxEnd` hooks will not be invoked. OnSystemCallStartHook = func() - // OnSystemCallStartHookV2 is called when a system call is about to be executed. Refer - // to `OnSystemCallStartHook` for more information. + // OnSystemCallStartHookV2 is called when a system call is about to be executed. + // Refer to `OnSystemCallStartHook` for more information. OnSystemCallStartHookV2 = func(vm *VMContext) - // OnSystemCallEndHook is called when a system call has finished executing. Today, - // this hook is invoked when the EIP-4788 system call is about to be executed to set the - // beacon block root. + // OnSystemCallEndHook is called when a system call has finished executing. + // Today, this hook is invoked when the EIP-4788 system call is about to be + // executed to set the beacon block root. OnSystemCallEndHook = func() /* @@ -183,9 +183,23 @@ type ( // StorageChangeHook is called when the storage of an account changes. StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash) + // ColdStorageReadHook is called before a previously-unread storage slot is read. + ColdStorageReadHook = func(common.Address, common.Hash) + + // ColdAccountReadHook is called before an previously-unread account is read. + ColdAccountReadHook = func(address common.Address) + + SelfDestructHook = func(address common.Address) + // LogHook is called when a log is emitted. LogHook = func(log *types.Log) + // AccountReadHook is called when the account is accessed. + AccountReadHook = func(addr common.Address) + + // StorageReadHook is called when the storage slot is accessed. + StorageReadHook = func(addr common.Address, slot common.Hash) + // BlockHashReadHook is called when EVM reads the blockhash of a block. BlockHashReadHook = func(blockNumber uint64, hash common.Hash) ) @@ -199,6 +213,7 @@ type Hooks struct { OnOpcode OpcodeHook OnFault FaultHook OnGasChange GasChangeHook + // Chain events OnBlockchainInit BlockchainInitHook OnClose CloseHook @@ -209,14 +224,24 @@ type Hooks struct { OnSystemCallStart OnSystemCallStartHook OnSystemCallStartV2 OnSystemCallStartHookV2 OnSystemCallEnd OnSystemCallEndHook - // State events - OnBalanceChange BalanceChangeHook - OnNonceChange NonceChangeHook - OnNonceChangeV2 NonceChangeHookV2 - OnCodeChange CodeChangeHook - OnCodeChangeV2 CodeChangeHookV2 - OnStorageChange StorageChangeHook - OnLog LogHook + + OnPreTxExecutionDone func() // called after pre-tx system contracts are invoked + OnBlockFinalization func() // called after post-tx system contracts and consensus finalization are invoked + + // State mutation events + OnBalanceChange BalanceChangeHook + OnNonceChange NonceChangeHook + OnNonceChangeV2 NonceChangeHookV2 + OnCodeChange CodeChangeHook + OnCodeChangeV2 CodeChangeHookV2 + OnStorageChange StorageChangeHook + OnLog LogHook + OnSelfDestructChange SelfDestructHook + + // State access events + OnAccountRead AccountReadHook + OnStorageRead StorageReadHook + // Block hash read OnBlockHashRead BlockHashReadHook } @@ -233,57 +258,74 @@ const ( // Issuance // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block. BalanceIncreaseRewardMineUncle BalanceChangeReason = 1 + // BalanceIncreaseRewardMineBlock is a reward for mining a block. BalanceIncreaseRewardMineBlock BalanceChangeReason = 2 + // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain. BalanceIncreaseWithdrawal BalanceChangeReason = 3 + // BalanceIncreaseGenesisBalance is ether allocated at the genesis block. BalanceIncreaseGenesisBalance BalanceChangeReason = 4 // Transaction fees - // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance. + // BalanceIncreaseRewardTransactionFee is the transaction tip increasing + // block builder's balance. BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5 + // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction. // Part of this gas will be burnt as per EIP-1559 rules. BalanceDecreaseGasBuy BalanceChangeReason = 6 + // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution. BalanceIncreaseGasReturn BalanceChangeReason = 7 // DAO fork // BalanceIncreaseDaoContract is ether sent to the DAO refund contract. BalanceIncreaseDaoContract BalanceChangeReason = 8 - // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract. + + // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved + // to the refund contract. BalanceDecreaseDaoAccount BalanceChangeReason = 9 // BalanceChangeTransfer is ether transferred via a call. // it is a decrease for the sender and an increase for the recipient. BalanceChangeTransfer BalanceChangeReason = 10 + // BalanceChangeTouchAccount is a transfer of zero value. It is only there to // touch-create an account. BalanceChangeTouchAccount BalanceChangeReason = 11 - // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account. + // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a + // selfdestructing account. BalanceIncreaseSelfdestruct BalanceChangeReason = 12 + // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct. BalanceDecreaseSelfdestruct BalanceChangeReason = 13 + // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed // account within the same tx (captured at end of tx). // Note it doesn't account for a self-destruct which appoints itself as recipient. BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 - // BalanceChangeRevert is emitted when the balance is reverted back to a previous value due to call failure. - // It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal). + // BalanceChangeRevert is emitted when the balance is reverted back to a + // previous value due to call failure. + // + // It is only emitted when the tracer has opted in to use the journaling + // wrapper (WrapWithJournal). BalanceChangeRevert BalanceChangeReason = 15 ) // GasChangeReason is used to indicate the reason for a gas change, useful // for tracing and reporting. // -// There is essentially two types of gas changes, those that can be emitted once per transaction -// and those that can be emitted on a call basis, so possibly multiple times per transaction. +// There is essentially two types of gas changes, those that can be emitted +// once per transaction and those that can be emitted on a call basis, so possibly +// multiple times per transaction. // -// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted -// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. +// They can be recognized easily by their name, those that start with `GasChangeTx` +// are emitted once per transaction, while those that start with `GasChangeCall` +// are emitted on a call basis. type GasChangeReason byte //go:generate go run golang.org/x/tools/cmd/stringer -type=GasChangeReason -trimprefix=GasChange -output gen_gas_change_reason_stringer.go @@ -291,61 +333,100 @@ type GasChangeReason byte const ( GasChangeUnspecified GasChangeReason = 0 - // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only - // one such gas change per transaction. + // GasChangeTxInitialBalance is the initial balance for the call which will + // be equal to the gasLimit of the call. There is only one such gas change + // per transaction. GasChangeTxInitialBalance GasChangeReason = 1 - // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is - // always exactly one of those per transaction. + + // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the + // intrinsic cost of the transaction, there is always exactly one of those + // per transaction. GasChangeTxIntrinsicGas GasChangeReason = 2 - // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) - // this generates an increase in gas. There is at most one of such gas change per transaction. + + // GasChangeTxRefunds is the sum of all refunds which happened during the tx + // execution (e.g. storage slot being cleared). this generates an increase in + // gas. There is at most one of such gas change per transaction. GasChangeTxRefunds GasChangeReason = 3 - // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned - // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas - // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. - // There is at most one of such gas change per transaction. + + // GasChangeTxLeftOverReturned is the amount of gas left over at the end of + // transaction's execution that will be returned to the chain. This change + // will always be a negative change as we "drain" left over gas towards 0. + // If there was no gas left at the end of execution, no such even will be + // emitted. The returned gas's value in Wei is returned to caller. There is + // at most one of such gas change per transaction. GasChangeTxLeftOverReturned GasChangeReason = 4 - // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only - // one such gas change per call. + // GasChangeCallInitialBalance is the initial balance for the call which + // will be equal to the gasLimit of the call. There is only one such gas + // change per call. GasChangeCallInitialBalance GasChangeReason = 5 - // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always - // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even - // will be emitted. + + // GasChangeCallLeftOverReturned is the amount of gas left over that will + // be returned to the caller, this change will always be a negative change + // as we "drain" left over gas towards 0. If there was no gas left at the + // end of execution, no such even will be emitted. GasChangeCallLeftOverReturned GasChangeReason = 6 - // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it - // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. - // If there was no gas left to be refunded, no such even will be emitted. + + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded + // to the call after the child call execution it executed completed. This + // value is always positive as we are giving gas back to the you, the left over + // gas of the child. If there was no gas left to be refunded, no such event + // will be emitted. GasChangeCallLeftOverRefunded GasChangeReason = 7 - // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. + + // GasChangeCallContractCreation is the amount of gas that will be burned + // for a CREATE. GasChangeCallContractCreation GasChangeReason = 8 - // GasChangeCallContractCreation2 is the amount of gas that will be burned for a CREATE2. + + // GasChangeCallContractCreation2 is the amount of gas that will be burned + // for a CREATE2. GasChangeCallContractCreation2 GasChangeReason = 9 - // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. + + // GasChangeCallCodeStorage is the amount of gas that will be charged for + // code storage. GasChangeCallCodeStorage GasChangeReason = 10 - // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was - // performed can be check by `OnOpcode` handling. + + // GasChangeCallOpCode is the amount of gas that will be charged for an opcode + // executed by the EVM, exact opcode that was performed can be check by + // `OnOpcode` handling. GasChangeCallOpCode GasChangeReason = 11 - // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. + + // GasChangeCallPrecompiledContract is the amount of gas that will be charged + // for a precompiled contract execution. GasChangeCallPrecompiledContract GasChangeReason = 12 - // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. + + // GasChangeCallStorageColdAccess is the amount of gas that will be charged + // for a cold storage access as controlled by EIP2929 rules. GasChangeCallStorageColdAccess GasChangeReason = 13 - // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. + + // GasChangeCallFailedExecution is the burning of the remaining gas when the + // execution failed without a revert. GasChangeCallFailedExecution GasChangeReason = 14 - // GasChangeWitnessContractInit flags the event of adding to the witness during the contract creation initialization step. + + // GasChangeWitnessContractInit flags the event of adding to the witness + // during the contract creation initialization step. GasChangeWitnessContractInit GasChangeReason = 15 - // GasChangeWitnessContractCreation flags the event of adding to the witness during the contract creation finalization step. + + // GasChangeWitnessContractCreation flags the event of adding to the witness + // during the contract creation finalization step. GasChangeWitnessContractCreation GasChangeReason = 16 - // GasChangeWitnessCodeChunk flags the event of adding one or more contract code chunks to the witness. + + // GasChangeWitnessCodeChunk flags the event of adding one or more contract + // code chunks to the witness. GasChangeWitnessCodeChunk GasChangeReason = 17 - // GasChangeWitnessContractCollisionCheck flags the event of adding to the witness when checking for contract address collision. + + // GasChangeWitnessContractCollisionCheck flags the event of adding to the + // witness when checking for contract address collision. GasChangeWitnessContractCollisionCheck GasChangeReason = 18 - // GasChangeTxDataFloor is the amount of extra gas the transaction has to pay to reach the minimum gas requirement for the - // transaction data. This change will always be a negative change. + + // GasChangeTxDataFloor is the amount of extra gas the transaction has to + // pay to reach the minimum gas requirement for the transaction data. + // This change will always be a negative change. GasChangeTxDataFloor GasChangeReason = 19 - // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as - // it will be "manually" tracked by a direct emit of the gas change event. + // GasChangeIgnored is a special value that can be used to indicate that + // the gas change should be ignored as it will be "manually" tracked by + // a direct emit of the gas change event. GasChangeIgnored GasChangeReason = 0xFF ) @@ -369,12 +450,16 @@ const ( // NonceChangeNewContract is the nonce change of a newly created contract. NonceChangeNewContract NonceChangeReason = 4 - // NonceChangeTransaction is the nonce change due to a EIP-7702 authorization. + // NonceChangeAuthorization is the nonce change due to a EIP-7702 authorization. NonceChangeAuthorization NonceChangeReason = 5 - // NonceChangeRevert is emitted when the nonce is reverted back to a previous value due to call failure. - // It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal). + // NonceChangeRevert is emitted when the nonce is reverted back to a previous + // value due to call failure. It is only emitted when the tracer has opted in + // to use the journaling wrapper (WrapWithJournal). NonceChangeRevert NonceChangeReason = 6 + + // NonceChangeSelfdestruct is emitted when the nonce is reset to zero due to a self-destruct + NonceChangeSelfdestruct NonceChangeReason = 7 ) // CodeChangeReason is used to indicate the reason for a code change. @@ -385,22 +470,26 @@ type CodeChangeReason byte const ( CodeChangeUnspecified CodeChangeReason = 0 - // CodeChangeContractCreation is when a new contract is deployed via CREATE/CREATE2 operations. + // CodeChangeContractCreation is when a new contract is deployed via + // CREATE/CREATE2 operations. CodeChangeContractCreation CodeChangeReason = 1 - // CodeChangeGenesis is when contract code is set during blockchain genesis or initial setup. + // CodeChangeGenesis is when contract code is set during blockchain genesis + // or initial setup. CodeChangeGenesis CodeChangeReason = 2 // CodeChangeAuthorization is when code is set via EIP-7702 Set Code Authorization. CodeChangeAuthorization CodeChangeReason = 3 - // CodeChangeAuthorizationClear is when EIP-7702 delegation is cleared by setting to zero address. + // CodeChangeAuthorizationClear is when EIP-7702 delegation is cleared by + // setting to zero address. CodeChangeAuthorizationClear CodeChangeReason = 4 // CodeChangeSelfDestruct is when contract code is cleared due to self-destruct. CodeChangeSelfDestruct CodeChangeReason = 5 - // CodeChangeRevert is emitted when the code is reverted back to a previous value due to call failure. - // It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal). + // CodeChangeRevert is emitted when the code is reverted back to a previous + // value due to call failure. It is only emitted when the tracer has opted + // in to use the journaling wrapper (WrapWithJournal). CodeChangeRevert CodeChangeReason = 6 ) diff --git a/core/tracing/journal.go b/core/tracing/journal.go index 62a70d6c272..87d1d2ad7da 100644 --- a/core/tracing/journal.go +++ b/core/tracing/journal.go @@ -42,7 +42,9 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { return nil, errors.New("wrapping nil tracer") } // No state change to journal, return the wrapped hooks as is - if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnCodeChangeV2 == nil && hooks.OnStorageChange == nil { + if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && + hooks.OnCodeChange == nil && hooks.OnCodeChangeV2 == nil && hooks.OnStorageChange == nil { + // TODO(sina) hooks.OnLog should also be handled here return hooks, nil } if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil { @@ -56,11 +58,14 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { wrapped := *hooks // Create journal - j := &journal{hooks: hooks} + j := &journal{ + hooks: hooks, + } // Scope hooks need to be re-implemented. wrapped.OnTxEnd = j.OnTxEnd wrapped.OnEnter = j.OnEnter wrapped.OnExit = j.OnExit + // Wrap state change hooks. if hooks.OnBalanceChange != nil { wrapped.OnBalanceChange = j.OnBalanceChange @@ -69,6 +74,7 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { // Regardless of which hook version is used in the tracer, // the journal will want to capture the nonce change reason. wrapped.OnNonceChangeV2 = j.OnNonceChangeV2 + // A precaution to ensure EVM doesn't call both hooks. wrapped.OnNonceChange = nil } @@ -81,7 +87,6 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { if hooks.OnStorageChange != nil { wrapped.OnStorageChange = j.OnStorageChange } - return &wrapped, nil } @@ -148,7 +153,11 @@ func (j *journal) OnExit(depth int, output []byte, gasUsed uint64, err error, re } func (j *journal) OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) { - j.entries = append(j.entries, balanceChange{addr: addr, prev: prev, new: new}) + j.entries = append(j.entries, balanceChange{ + addr: addr, + prev: prev, + new: new, + }) if j.hooks.OnBalanceChange != nil { j.hooks.OnBalanceChange(addr, prev, new, reason) } @@ -158,7 +167,11 @@ func (j *journal) OnNonceChangeV2(addr common.Address, prev, new uint64, reason // When a contract is created, the nonce of the creator is incremented. // This change is not reverted when the creation fails. if reason != NonceChangeContractCreator { - j.entries = append(j.entries, nonceChange{addr: addr, prev: prev, new: new}) + j.entries = append(j.entries, nonceChange{ + addr: addr, + prev: prev, + new: new, + }) } if j.hooks.OnNonceChangeV2 != nil { j.hooks.OnNonceChangeV2(addr, prev, new, reason) @@ -194,7 +207,12 @@ func (j *journal) OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, } func (j *journal) OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) { - j.entries = append(j.entries, storageChange{addr: addr, slot: slot, prev: prev, new: new}) + j.entries = append(j.entries, storageChange{ + addr: addr, + slot: slot, + prev: prev, + new: new, + }) if j.hooks.OnStorageChange != nil { j.hooks.OnStorageChange(addr, slot, prev, new) } diff --git a/core/tracing/journal_test.go b/core/tracing/journal_test.go index e00447f5f3e..9f9fa275653 100644 --- a/core/tracing/journal_test.go +++ b/core/tracing/journal_test.go @@ -63,7 +63,7 @@ func (t *testTracer) OnCodeChangeV2(addr common.Address, prevCodeHash common.Has } func (t *testTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) { - t.t.Logf("OnStorageCodeChange(%v, %v, %v -> %v)", addr, slot, prev, new) + t.t.Logf("OnStorageChange(%v, %v, %v -> %v)", addr, slot, prev, new) if t.storage == nil { t.storage = make(map[common.Hash]common.Hash) } @@ -76,7 +76,12 @@ func (t *testTracer) OnStorageChange(addr common.Address, slot common.Hash, prev func TestJournalIntegration(t *testing.T) { tr := &testTracer{t: t} - wr, err := WrapWithJournal(&Hooks{OnBalanceChange: tr.OnBalanceChange, OnNonceChange: tr.OnNonceChange, OnCodeChange: tr.OnCodeChange, OnStorageChange: tr.OnStorageChange}) + wr, err := WrapWithJournal(&Hooks{ + OnBalanceChange: tr.OnBalanceChange, + OnNonceChange: tr.OnNonceChange, + OnCodeChange: tr.OnCodeChange, + OnStorageChange: tr.OnStorageChange, + }) if err != nil { t.Fatalf("failed to wrap test tracer: %v", err) } diff --git a/core/types.go b/core/types.go index bed20802ab5..0f422a14e06 100644 --- a/core/types.go +++ b/core/types.go @@ -32,7 +32,7 @@ type Validator interface { ValidateBody(block *types.Block) error // ValidateState validates the given statedb and optionally the process result. - ValidateState(block *types.Block, state *state.StateDB, res *ProcessResult, stateless bool) error + ValidateState(block *types.Block, state *state.StateDB, res *ProcessResult, validateStateRoot, stateless bool) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -57,4 +57,5 @@ type ProcessResult struct { Requests [][]byte Logs []*types.Log GasUsed uint64 + Error error } diff --git a/core/types/bal/bal.go b/core/types/bal/bal.go index fca54f7681f..fa1f26f5908 100644 --- a/core/types/bal/bal.go +++ b/core/types/bal/bal.go @@ -18,143 +18,492 @@ package bal import ( "bytes" - "maps" - + "encoding/json" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" + "maps" ) +// idxAccessListBuilder is responsible for producing the state accesses and +// reads recorded within the scope of a single index in the access list. +type idxAccessListBuilder struct { + // stores the previous values of any account data that was modified in the + // current index. + prestates map[common.Address]*accountIdxPrestate + + // a stack which maintains a set of state mutations/reads for each EVM + // execution frame. Entering a frame appends an intermediate access list + // and terminating a frame merges the accesses/modifications into the + // intermediate access list of the calling frame. + accessesStack []map[common.Address]*constructionAccountAccess +} + +func newAccessListBuilder() *idxAccessListBuilder { + return &idxAccessListBuilder{ + make(map[common.Address]*accountIdxPrestate), + []map[common.Address]*constructionAccountAccess{ + make(map[common.Address]*constructionAccountAccess), + }, + } +} + +func (c *idxAccessListBuilder) storageRead(address common.Address, key common.Hash) { + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } + acctAccesses := c.accessesStack[len(c.accessesStack)-1][address] + acctAccesses.StorageRead(key) +} + +func (c *idxAccessListBuilder) accountRead(address common.Address) { + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } +} + +func (c *idxAccessListBuilder) storageWrite(address common.Address, key, prevVal, newVal common.Hash) { + if _, ok := c.prestates[address]; !ok { + c.prestates[address] = &accountIdxPrestate{} + } + if c.prestates[address].storage == nil { + c.prestates[address].storage = make(map[common.Hash]common.Hash) + } + if _, ok := c.prestates[address].storage[key]; !ok { + c.prestates[address].storage[key] = prevVal + } + + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } + acctAccesses := c.accessesStack[len(c.accessesStack)-1][address] + acctAccesses.StorageWrite(key, prevVal, newVal) +} + +func (c *idxAccessListBuilder) balanceChange(address common.Address, prev, cur *uint256.Int) { + if _, ok := c.prestates[address]; !ok { + c.prestates[address] = &accountIdxPrestate{} + } + if c.prestates[address].balance == nil { + c.prestates[address].balance = prev + } + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } + acctAccesses := c.accessesStack[len(c.accessesStack)-1][address] + acctAccesses.BalanceChange(cur) +} + +func (c *idxAccessListBuilder) codeChange(address common.Address, prev, cur []byte) { + // auth unset and selfdestruct pass code change as 'nil' + // however, internally in the access list accumulation of state changes, + // a nil field on an account means that it was never modified in the block. + if cur == nil { + cur = []byte{} + } + + if _, ok := c.prestates[address]; !ok { + c.prestates[address] = &accountIdxPrestate{} + } + if c.prestates[address].code == nil { + c.prestates[address].code = prev + } + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } + acctAccesses := c.accessesStack[len(c.accessesStack)-1][address] + + acctAccesses.CodeChange(cur) +} + +// selfDestruct is invoked when an account which has been created and invoked +// SENDALL in the same transaction is removed as part of transaction finalization. +// +// Any storage accesses/modifications performed at the contract during execution +// are retained in the block access list as state reads. +func (c *idxAccessListBuilder) selfDestruct(address common.Address) { + // convert all the account storage writes to reads, preserve the existing reads + access := c.accessesStack[len(c.accessesStack)-1][address] + for key, _ := range access.storageMutations { + if access.storageReads == nil { + access.storageReads = make(map[common.Hash]struct{}) + } + access.storageReads[key] = struct{}{} + } + + access.storageMutations = nil +} + +func (c *idxAccessListBuilder) nonceChange(address common.Address, prev, cur uint64) { + if _, ok := c.prestates[address]; !ok { + c.prestates[address] = &accountIdxPrestate{} + } + if c.prestates[address].nonce == nil { + c.prestates[address].nonce = &prev + } + if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok { + c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{} + } + acctAccesses := c.accessesStack[len(c.accessesStack)-1][address] + acctAccesses.NonceChange(cur) +} + +// enterScope is called after a new EVM frame has been entered. +func (c *idxAccessListBuilder) enterScope() { + c.accessesStack = append(c.accessesStack, make(map[common.Address]*constructionAccountAccess)) +} + +// exitScope is called after an EVM call scope terminates. If the call scope +// terminates with an error: +// * the scope's state accesses are added to the calling scope's access list +// * mutated accounts/storage are added into the calling scope's access list as state accesses +// * the state mutations tracked in the parent scope are un-modified +func (c *idxAccessListBuilder) exitScope(evmErr bool) { + // all storage writes in the child scope are converted into reads + // if there were no storage writes, the account is reported in the BAL as a read (if it wasn't already in the BAL and/or mutated previously) + childAccessList := c.accessesStack[len(c.accessesStack)-1] + parentAccessList := c.accessesStack[len(c.accessesStack)-2] + + for addr, childAccess := range childAccessList { + if _, ok := parentAccessList[addr]; ok { + } else { + parentAccessList[addr] = &constructionAccountAccess{} + } + if evmErr { + parentAccessList[addr].MergeReads(childAccess) + } else { + parentAccessList[addr].Merge(childAccess) + } + } + + c.accessesStack = c.accessesStack[:len(c.accessesStack)-1] +} + +// finalise returns the net state mutations at the access list index as well as +// state which was accessed. The idxAccessListBuilder instance should be discarded +// after calling finalise. +func (a *idxAccessListBuilder) finalise() (*StateDiff, StateAccesses) { + diff := &StateDiff{make(map[common.Address]*AccountMutations)} + stateAccesses := make(StateAccesses) + + for addr, access := range a.accessesStack[0] { + // remove any mutations from the access list with no net difference vs the tx prestate value + if access.nonce != nil && *a.prestates[addr].nonce == *access.nonce { + access.nonce = nil + } + if access.balance != nil && a.prestates[addr].balance.Eq(access.balance) { + access.balance = nil + } + + if access.code != nil && bytes.Equal(access.code, a.prestates[addr].code) { + access.code = nil + } + if access.storageMutations != nil { + for key, val := range access.storageMutations { + if a.prestates[addr].storage[key] == val { + delete(access.storageMutations, key) + access.storageReads[key] = struct{}{} + } + } + if len(access.storageMutations) == 0 { + access.storageMutations = nil + } + } + + // if the account has no net mutations against the tx prestate, only include + // it in the state read set + if len(access.code) == 0 && access.nonce == nil && access.balance == nil && len(access.storageMutations) == 0 { + stateAccesses[addr] = make(map[common.Hash]struct{}) + if access.storageReads != nil { + stateAccesses[addr] = access.storageReads + } + continue + } + + stateAccesses[addr] = access.storageReads + diff.Mutations[addr] = &AccountMutations{ + Balance: access.balance, + Nonce: access.nonce, + Code: access.code, + StorageWrites: access.storageMutations, + } + } + + return diff, stateAccesses +} + +// FinaliseIdxChanges records all pending state mutations/accesses in the +// access list at the given index. The set of pending state mutations/accesse are +// then emptied. +func (c *AccessListBuilder) FinaliseIdxChanges(idx uint16) { + pendingDiff, pendingAccesses := c.idxBuilder.finalise() + c.idxBuilder = newAccessListBuilder() + + // if any of the newly-written storage slots were previously + // accessed, they must be removed from the accessed state set. + for addr, pendingAcctDiff := range pendingDiff.Mutations { + finalizedAcctChanges, ok := c.FinalizedAccesses[addr] + if !ok { + finalizedAcctChanges = &ConstructionAccountAccesses{} + c.FinalizedAccesses[addr] = finalizedAcctChanges + } + + if pendingAcctDiff.Nonce != nil { + if finalizedAcctChanges.NonceChanges == nil { + finalizedAcctChanges.NonceChanges = make(map[uint16]uint64) + } + finalizedAcctChanges.NonceChanges[idx] = *pendingAcctDiff.Nonce + } + if pendingAcctDiff.Balance != nil { + if finalizedAcctChanges.BalanceChanges == nil { + finalizedAcctChanges.BalanceChanges = make(map[uint16]*uint256.Int) + } + finalizedAcctChanges.BalanceChanges[idx] = pendingAcctDiff.Balance + } + if pendingAcctDiff.Code != nil { + if finalizedAcctChanges.CodeChanges == nil { + finalizedAcctChanges.CodeChanges = make(map[uint16]CodeChange) + } + finalizedAcctChanges.CodeChanges[idx] = CodeChange{idx, pendingAcctDiff.Code} + } + if pendingAcctDiff.StorageWrites != nil { + if finalizedAcctChanges.StorageWrites == nil { + finalizedAcctChanges.StorageWrites = make(map[common.Hash]map[uint16]common.Hash) + } + for key, val := range pendingAcctDiff.StorageWrites { + if _, ok := finalizedAcctChanges.StorageWrites[key]; !ok { + finalizedAcctChanges.StorageWrites[key] = make(map[uint16]common.Hash) + } + finalizedAcctChanges.StorageWrites[key][idx] = val + + // TODO: investigate why commenting out the check here, and the corresponding + // check under accesses causes GeneralStateTests blockchain tests to fail. + // They should only contain one tx per test. + // + // key could have been read in a previous tx, delete it from the read set here + if _, ok := finalizedAcctChanges.StorageReads[key]; ok { + delete(finalizedAcctChanges.StorageReads, key) + } + } + } + } + // record pending accesses in the BAL access set unless they were + // already written in a previous index + for addr, pendingAccountAccesses := range pendingAccesses { + finalizedAcctAccesses, ok := c.FinalizedAccesses[addr] + if !ok { + finalizedAcctAccesses = &ConstructionAccountAccesses{} + c.FinalizedAccesses[addr] = finalizedAcctAccesses + } + + for key := range pendingAccountAccesses { + if _, ok := finalizedAcctAccesses.StorageWrites[key]; ok { + continue + } + if finalizedAcctAccesses.StorageReads == nil { + finalizedAcctAccesses.StorageReads = make(map[common.Hash]struct{}) + } + finalizedAcctAccesses.StorageReads[key] = struct{}{} + } + } + c.lastFinalizedMutations = pendingDiff + c.lastFinalizedAccesses = pendingAccesses +} + +func (c *AccessListBuilder) StorageRead(address common.Address, key common.Hash) { + c.idxBuilder.storageRead(address, key) +} +func (c *AccessListBuilder) AccountRead(address common.Address) { + c.idxBuilder.accountRead(address) +} +func (c *AccessListBuilder) StorageWrite(address common.Address, key, prevVal, newVal common.Hash) { + c.idxBuilder.storageWrite(address, key, prevVal, newVal) +} +func (c *AccessListBuilder) BalanceChange(address common.Address, prev, cur *uint256.Int) { + c.idxBuilder.balanceChange(address, prev, cur) +} +func (c *AccessListBuilder) NonceChange(address common.Address, prev, cur uint64) { + c.idxBuilder.nonceChange(address, prev, cur) +} +func (c *AccessListBuilder) CodeChange(address common.Address, prev, cur []byte) { + c.idxBuilder.codeChange(address, prev, cur) +} +func (c *AccessListBuilder) SelfDestruct(address common.Address) { + c.idxBuilder.selfDestruct(address) +} + +func (c *AccessListBuilder) EnterScope() { + c.idxBuilder.enterScope() +} +func (c *AccessListBuilder) ExitScope(executionErr bool) { + c.idxBuilder.exitScope(executionErr) +} + // CodeChange contains the runtime bytecode deployed at an address and the // transaction index where the deployment took place. type CodeChange struct { - TxIndex uint16 - Code []byte `json:"code,omitempty"` + TxIdx uint16 + Code []byte `json:"code,omitempty"` } -// ConstructionAccountAccess contains post-block account state for mutations as well as +// ConstructionAccountAccesses contains post-block account state for mutations as well as // all storage keys that were read during execution. It is used when building block // access list during execution. -type ConstructionAccountAccess struct { +type ConstructionAccountAccesses 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"` + CodeChanges map[uint16]CodeChange } -// NewConstructionAccountAccess initializes the account access object. -func NewConstructionAccountAccess() *ConstructionAccountAccess { - return &ConstructionAccountAccess{ - StorageWrites: make(map[common.Hash]map[uint16]common.Hash), - StorageReads: make(map[common.Hash]struct{}), - BalanceChanges: make(map[uint16]*uint256.Int), - NonceChanges: make(map[uint16]uint64), - } -} +// constructionAccountAccess contains fields for an account which were modified +// during execution of the current access list index. +// It also accumulates a set of storage slots which were accessed but not +// modified. +type constructionAccountAccess struct { + code []byte + nonce *uint64 + balance *uint256.Int -// ConstructionBlockAccessList contains post-block modified state and some state accessed -// in execution (account addresses and storage keys). -type ConstructionBlockAccessList struct { - Accounts map[common.Address]*ConstructionAccountAccess + storageMutations map[common.Hash]common.Hash + storageReads map[common.Hash]struct{} } -// NewConstructionBlockAccessList instantiates an empty access list. -func NewConstructionBlockAccessList() ConstructionBlockAccessList { - return ConstructionBlockAccessList{ - Accounts: make(map[common.Address]*ConstructionAccountAccess), +// Merge adds the accesses/mutations from other into the calling instance. If +func (c *constructionAccountAccess) Merge(other *constructionAccountAccess) { + if other.code != nil { + c.code = other.code + } + if other.nonce != nil { + c.nonce = other.nonce + } + if other.balance != nil { + c.balance = other.balance + } + if other.storageMutations != nil { + if c.storageMutations == nil { + c.storageMutations = make(map[common.Hash]common.Hash) + } + for key, val := range other.storageMutations { + c.storageMutations[key] = val + delete(c.storageReads, key) + } + } + if other.storageReads != nil { + if c.storageReads == nil { + c.storageReads = make(map[common.Hash]struct{}) + } + // TODO: if the state was mutated in the caller, don't add it to the caller's reads. + // need to have a test case for this, verify it fails in the current state, and then fix this bug. + for key, val := range other.storageReads { + c.storageReads[key] = val + } } } -// 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() +// MergeReads merges accesses from a reverted execution from: +// * any reads/writes from the reverted frame which weren't mutated +// in the current frame, are merged into the current frame as reads. +func (c *constructionAccountAccess) MergeReads(other *constructionAccountAccess) { + if other.storageMutations != nil { + if c.storageReads == nil { + c.storageReads = make(map[common.Hash]struct{}) + } + for key, _ := range other.storageMutations { + if _, ok := c.storageMutations[key]; ok { + continue + } + c.storageReads[key] = struct{}{} + } + } + if other.storageReads != nil { + if c.storageReads == nil { + c.storageReads = make(map[common.Hash]struct{}) + } + for key := range other.storageReads { + if _, ok := c.storageMutations[key]; ok { + continue + } + c.storageReads[key] = struct{}{} + } } } -// 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() +func (c *constructionAccountAccess) StorageRead(key common.Hash) { + if c.storageReads == nil { + c.storageReads = make(map[common.Hash]struct{}) } - if _, ok := b.Accounts[address].StorageWrites[key]; ok { - return + if _, ok := c.storageMutations[key]; !ok { + c.storageReads[key] = struct{}{} } - b.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 *constructionAccountAccess) StorageWrite(key, prevVal, newVal common.Hash) { + if c.storageMutations == nil { + c.storageMutations = make(map[common.Hash]common.Hash) } - if _, ok := b.Accounts[address].StorageWrites[key]; !ok { - b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash) - } - b.Accounts[address].StorageWrites[key][txIdx] = value + c.storageMutations[key] = newVal + // a key can be first read and later written, but it must only show up + // in either read or write sets, not both. + // + // the caller should not + // call StorageRead on a slot that was already written + delete(c.storageReads, key) +} - delete(b.Accounts[address].StorageReads, key) +func (c *constructionAccountAccess) BalanceChange(cur *uint256.Int) { + c.balance = cur } -// 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() - } - b.Accounts[address].CodeChange = &CodeChange{ - TxIndex: txIndex, - Code: bytes.Clone(code), - } +func (c *constructionAccountAccess) CodeChange(cur []byte) { + c.code = cur } -// 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() - } - b.Accounts[address].NonceChanges[txIdx] = postNonce +func (c *constructionAccountAccess) NonceChange(cur uint64) { + c.nonce = &cur } -// 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() - } - b.Accounts[address].BalanceChanges[txIdx] = balance.Clone() +// AccessListBuilder is used to build an EIP-7928 block access list +type AccessListBuilder struct { + FinalizedAccesses map[common.Address]*ConstructionAccountAccesses + + idxBuilder *idxAccessListBuilder + + lastFinalizedMutations *StateDiff + lastFinalizedAccesses StateAccesses } -// PrettyPrint returns a human-readable representation of the access list -func (b *ConstructionBlockAccessList) PrettyPrint() string { - enc := b.toEncodingObj() - return enc.PrettyPrint() +// NewAccessListBuilder instantiates an empty access list. +func NewAccessListBuilder() *AccessListBuilder { + return &AccessListBuilder{ + make(map[common.Address]*ConstructionAccountAccesses), + newAccessListBuilder(), + nil, + nil, + } } // Copy returns a deep copy of the access list. -func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList { - res := NewConstructionBlockAccessList() - for addr, aa := range b.Accounts { - var aaCopy ConstructionAccountAccess +func (c *AccessListBuilder) Copy() *AccessListBuilder { + res := NewAccessListBuilder() + for addr, aa := range c.FinalizedAccesses { + var aaCopy ConstructionAccountAccesses slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites)) for key, m := range aa.StorageWrites { @@ -170,13 +519,187 @@ func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList { aaCopy.BalanceChanges = balances aaCopy.NonceChanges = maps.Clone(aa.NonceChanges) - if aa.CodeChange != nil { - aaCopy.CodeChange = &CodeChange{ - TxIndex: aa.CodeChange.TxIndex, - Code: bytes.Clone(aa.CodeChange.Code), + codeChangesCopy := make(map[uint16]CodeChange) + for idx, codeChange := range aa.CodeChanges { + codeChangesCopy[idx] = CodeChange{ + TxIdx: idx, + Code: bytes.Clone(codeChange.Code), + } + } + res.FinalizedAccesses[addr] = &aaCopy + } + return res +} + +// FinalizedIdxChanges returns the state mutations and accesses recorded in the latest +// access list index that was finalized. +func (c *AccessListBuilder) FinalizedIdxChanges() (*StateDiff, StateAccesses) { + return c.lastFinalizedMutations, c.lastFinalizedAccesses +} + +// StateDiff contains state mutations occuring over one or more access list +// index. +type StateDiff struct { + Mutations map[common.Address]*AccountMutations `json:"Mutations,omitempty"` +} + +// StateAccesses contains a set of accounts/storage that were accessed during the +// execution of one or more access list indices. +type StateAccesses map[common.Address]map[common.Hash]struct{} + +// Merge combines adds the accesses from other into s. +func (s *StateAccesses) Merge(other StateAccesses) { + for addr, accesses := range other { + if _, ok := (*s)[addr]; !ok { + (*s)[addr] = make(map[common.Hash]struct{}) + } + for slot := range accesses { + (*s)[addr][slot] = struct{}{} + } + } +} + +// accountIdxPrestate records the account prestate at a access list index +// for components which were modified at that index. +type accountIdxPrestate struct { + balance *uint256.Int + nonce *uint64 + code ContractCode + storage map[common.Hash]common.Hash +} + +// AccountMutations contains mutations that were made to an account across +// one or more access list indices. +type AccountMutations struct { + Balance *uint256.Int `json:"Balance,omitempty"` + Nonce *uint64 `json:"Nonce,omitempty"` + Code ContractCode `json:"Code,omitempty"` + StorageWrites map[common.Hash]common.Hash `json:"StorageWrites,omitempty"` +} + +// String returns a human-readable JSON representation of the account mutations. +func (a *AccountMutations) String() string { + var res bytes.Buffer + enc := json.NewEncoder(&res) + enc.SetIndent("", " ") + enc.Encode(a) + return res.String() +} + +// Eq returns whether the calling instance is equal to the provided one. +func (a *AccountMutations) Eq(other *AccountMutations) bool { + if a.Balance != nil || other.Balance != nil { + if a.Balance == nil || other.Balance == nil { + return false + } + + if !a.Balance.Eq(other.Balance) { + return false + } + } + + if (len(a.Code) != 0 || len(other.Code) != 0) && !bytes.Equal(a.Code, other.Code) { + return false + } + + if a.Nonce != nil || other.Nonce != nil { + if a.Nonce == nil || other.Nonce == nil { + return false + } + + if *a.Nonce != *other.Nonce { + return false + } + } + + if a.StorageWrites != nil || other.StorageWrites != nil { + if a.StorageWrites == nil || other.StorageWrites == nil { + return false + } + + if !maps.Equal(a.StorageWrites, other.StorageWrites) { + return false + } + } + return true +} + +// Copy returns a deep-copy of the instance. +func (a *AccountMutations) Copy() *AccountMutations { + res := &AccountMutations{ + nil, + nil, + nil, + nil, + } + if a.Nonce != nil { + res.Nonce = new(uint64) + *res.Nonce = *a.Nonce + } + if a.Code != nil { + res.Code = bytes.Clone(a.Code) + } + if a.Balance != nil { + res.Balance = new(uint256.Int).Set(a.Balance) + } + if a.StorageWrites != nil { + res.StorageWrites = maps.Clone(a.StorageWrites) + } + return res +} + +// String returns the state diff as a formatted JSON string. +func (s *StateDiff) String() string { + var res bytes.Buffer + enc := json.NewEncoder(&res) + enc.SetIndent("", " ") + enc.Encode(s) + return res.String() +} + +// Merge merges the state changes present in next into the caller. After, +// the state of the caller is the aggregate diff through next. +func (s *StateDiff) Merge(next *StateDiff) { + for account, diff := range next.Mutations { + if mut, ok := s.Mutations[account]; ok { + if diff.Balance != nil { + mut.Balance = diff.Balance + } + if diff.Code != nil { + mut.Code = diff.Code } + if diff.Nonce != nil { + mut.Nonce = diff.Nonce + } + if len(diff.StorageWrites) > 0 { + if mut.StorageWrites == nil { + mut.StorageWrites = maps.Clone(diff.StorageWrites) + } else { + for key, val := range diff.StorageWrites { + mut.StorageWrites[key] = val + } + } + } + } else { + s.Mutations[account] = diff.Copy() } - res.Accounts[addr] = &aaCopy } - return &res +} + +// Copy returns a deep copy of the StateDiff +func (s *StateDiff) Copy() *StateDiff { + res := &StateDiff{make(map[common.Address]*AccountMutations)} + for addr, accountDiff := range s.Mutations { + cpy := accountDiff.Copy() + res.Mutations[addr] = cpy + } + return res +} + +// Copy returns a deep copy of the access list +func (e BlockAccessList) Copy() (res BlockAccessList) { + for _, accountAccess := range e { + res = append(res, accountAccess.Copy()) + } + return } diff --git a/core/types/bal/bal.rlp.hex b/core/types/bal/bal.rlp.hex new file mode 100644 index 00000000000..bc855f850de --- /dev/null +++ b/core/types/bal/bal.rlp.hex @@ -0,0 +1 @@ +fa0232eefa0232eada940000000000000000000000000000000000000001c0c0c0c0c0da940000000000000000000000000000000000000004c0c0c0c0c0f905ea940000000000000068f116a894984e2db1123eb395f9039bf845a02879eb18191584a3a1ebce36a8d948c75a6f8c9fda6da11e4a3fab1bd3ee1a7de3e203a00000000000000000000000000000010000000000000000000000000000010001f845a032d4f5dfd2de87dfd375caa9ece73a1836f808f322ae8165556b19ee9b6ee5bbe3e203a00000000000000000000000000000010000000000000000000000000000010001f845a0385bd69fd0a4bb474385b68e83f65ac3c4026b842acb9c32971f9e751b8ceca5e3e266a00000000000000000000000000000010000000000000000000000000000010001f845a051509531c62a021e8d735685092e25c29be839568e0bf54fe50af22d757d0fefe3e211a00000000000000000000000000000010000000000000000000000000000010001f845a0551298134701db706297bcbe9e61fa706d2ae3d72d7fa6f464cd605f7ff46b91e3e203a00000000000000000000000000000010000000000000000000000000000010001f845a06fd69f14432de6ca0fd7a0afe266c1d2f15066ac477e6c8e26d4d4e36dd555a0e3e203a00000000000000000000000000000010000000000000000000000000000010001f845a07becbfd691d229a8755150d84095d2057ea205d4824d58e890ecfd58b8ee81b1e3e203a00000000000000000000000000000010000000000000000000000000000010001f845a08af99cf03ac6af02fc168a4de01e4786c0f7ac8b3e4810e6a7e5d660129cec24e3e266a00000000000000000000000000000010000000000000000000000000000010001f845a0a761ee45024e359298929f02210f136ec8ea7ecc213d124d688df248e2083f92e3e203a00000000000000000000000000000010000000000000000000000000000010001f845a0d2359819549a4b8909616d11e73c544b513069e66dc67f9056089a917a5d8fbce3e203a00000000000000000000000000000010000000000000000000000000000010001f845a0d91096803defa942270be20b3387c1774041f198954ba5fd503984d4ac0e7a61e3e206a00000000000000000000000000000010000000000000000000000000000010001f845a0e35da6dba6b14447af96e3b74b05ce421142c82a91c8e13afefb555171486bf6e3e203a00000000000000000000000000000010000000000000000000000000000010001f845a0e8ad641b3ed88082e4cd76c62a2dad5ba22000b276acb6233329c9667a2c904ee3e203a00000000000000000000000000000010000000000000000000000000000010001f90231a004a3c4a0d9047623b8d7dcc3b677ed2c4d54685e57fe6cd104d802b4d4f7b7efa00d4a57882193ce57c7646ab152072e4c024e23f8bafb6689586cfffe5f974315a01709d7b25e94969bfaa391d9a630b815521992b8ffc51672a8fc80eacf0b55b5a0240b730d61c4bf13447525287d6827368bae79bdce1dc6db0d2b538df3d1cd37a0284cc95d48029f1784f0afc558ff3176af587a76478dd098c1c84124633e8582a062b6c76342a7681bbacde838bb2ebeb623de0cc0cb5b41e28543aca6b5176d59a06484b2e917f126ed3e49ecb98ccdfee0552978a4277c1f20aec531f2799d2adca079d7e14944620d9cd62c91d1b6eb1729494b6a264d6d4cfc3fde7657bd4a8ab0a07f37666151e0742dc4e1604442f707cc4e14a477e70516d04137158689a8fdf1a0998b7070b8aa97b737b95edaa42eeb988a498c0add57e71695bdc356e551b34ca0a67eb2745aace31d3a09ac0256d2d87a3104e1f8faee70aa77de29f3b036901fa0b43f6cc3aa7f4991e0cd46cc36dbfcf6df468811ff45fcf0243641caba09af25a0b9ef8724ad29084e233d76ffe54969b7f07e23e0a674aa3946f744cd10d35769a0e93f8b2d15d9079158e44d4119bec6d7f8b9c7fde7ae4dd9d8aca39386b0d812a0ed9adb6c793843819de3c018021715ffea8880c9d7ab503edbee94c165d36fa2a0ee835806d06236377e76760aac360a386a959b400c300a8f4b5119ac88d977b2a0fbd0261837df9af0ad3ec0cba83022d1bbc05d612551f8bb3b281a1ba1ba9ab5c0c0c0da940000000000001ff3684f28c67538d4d072c22734c0c0c0c0c0f6940000000000008359421a71a3d1f8c72921c8504dc0c0d5d482013a900000000000000000097f46dfb4f6e101c7c682013a8216a7c0f89f94000000000000aaeb6d7670e522a718067333cd4ec0f884a027731369f0182227d5ee3f30301231ccf0c3322575059918eb616a9bf26e980fa05344153e8ca27a513c3abedf923021a87531bbc83613ac09db5321eb1f931ae4a0a5e1f1ea720828095d7ab73f1c7e9411da82aee9bc5912a6db678c1278872869a0e9f6ea489f01977683b83865ec5011394dbbdb035734e086a69bb5804da3523cc0c0c0f9012494000000000004444c5dc75cb358380d2e3de08a90c0f90108a02251a267dd413cd2f816e4e39dccc617915aec866e0d2bcf079ee27af055df5fa045f1c2ca1bc0fcd8c740d5a8a3c1aea36b125fbf83148ce1efb1cfff6b88b362a045f1c2ca1bc0fcd8c740d5a8a3c1aea36b125fbf83148ce1efb1cfff6b88b364a045f1c2ca1bc0fcd8c740d5a8a3c1aea36b125fbf83148ce1efb1cfff6b88b365a0cec23763c1f03e9adaba802adb9cbe72eb9a7c221efa9496ee324863442cc600a0f001563bc94a5938a90a6eb9e7c9db7201706585fc453f514a4c32a74ac8fea9a0f001563bc94a5938a90a6eb9e7c9db7201706585fc453f514a4c32a74ac8feaba0f001563bc94a5938a90a6eb9e7c9db7201706585fc453f514a4c32a74ac8feacc0c0c0f8ca9400000000000c2e074ec69a0dfb2997ba6c7d2e1ef88ef845a01b7bdf7aaaf4a7bf4a4756f270ee99d9e9b6223012c1ac9bc423435181210951e3e239a0000000000000000000000000b8d0633c0b02e2fcb369e06715d073c26c3c82d5f845a01b7bdf7aaaf4a7bf4a4756f270ee99d9e9b6223012c1ac9bc423435181210952e3e239a0000000000000000000000000231b0ee14048e9dccd1d247744d114a4eb5e8e63e1a00dd4f47a3b6f87903b728feed7a23273bd8de58e4fa257d37a74a5881e5d92ffc0c0c0f8cb94000000000022d473030f116ddee9f6b43ac78ba3f88ff846a03bd1bc9f0c1caeea36d17918170b656242da38ab793ac7579de18100c8c2df4de4e381f9a0000000000001000068b19e59fffffffffffffffffffffffffffffffffffffffff845a0cd14cc5ae1bc0bd5d0ce6971ae58c160366f4e7c9af1891ede2bea70127122d1e3e208a00000000000008000000000007fffffffffffffffffffe61d10f967f8794fee23e1a0cdf6b22f99077db6230168c1d98f66ddbd6759b773c86de25e172d4e35b353c0c0c0c0f8719400000000009726632680fb29d3f7a9734e3010e2c0f842a00000000000000000000000000000000000000000000000000000000000000000a0de0d34d4f7bf3414f80d8d32502529d716ff1fa9f423cd1409d7f99d08fd075cd4d381e990000000000000000003dbfb9429ed8fc7c0c0ef940000000000efa780a8e6f50fc5de9c1497bfd175c0c0d5d482013a900000000000000000000001726124f520c0c0f8a5940000000071727de22e5e9d8baf0edac6f37da032f847f845a06df2cc124fb9eddfc698d0a07846758f68e88093a7a51be5f2bf8b7b005a825ee3e219a00000000000000000000000000000000000000000000000000000000000000001f842a00000000000000000000000000000000000000000000000000000000000000002a05e7790995149cd394adfae18934f7312b3f381581362ed0c970a982e2f17869cc0c0c0f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f8e1940000206329b97db379d5e1bf586bbdb969c63274c0f8c6a00000000000000000000000000000000000000000000000000000000000000035a01259640b4adb4af8dfa1fb1e61fcbedd02b012e121c8b41224ae42e6df833153a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca083e29ffce6d2fbf1f0c271d06886a38d0c891f440a61908907e91a556f6dd54fa093870757cb31b01a453ca904251af33e85caabe2631d491839de8262007e362da0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f9016c940000317bec33af037b5fab2028f52d14658f6a56f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e234a000000000000000000000000000000000000000000000000000000000000080b2f90108a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000aa012d911b54d832865fb42c73262858caaf8e30c53f33e1dc0fb0c2f5947b2c96ea0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca05e5777fab7622aff3c042c1ece74307c2e9d699a9da444f416c35f2e1def28a5a06568ffeb235291e33a361e55cdee2c50db5ab23b00145c8fac2f394be4192d64a09b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a0cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300c0c0c0f83b94000056f7000000ece9003ca63978907a00ffd100c0e1a08a544f7a90e767d9bea3a08f370e08d816168745881d640911c6f3d377888215c0c0c0f854940000a26b00c1f0df003000390027140000faa719c0c0f839d20390000000000000000032fd22d7fac8120ed20690000000000000000032fd63d809562f0ed21190000000000000000032fd724963187f0ec0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000001ba3e3e280a0ac0a5a14f347269715b618c8fa88f9f8ad15f1fc1f6116d3e28b20ca41229fd0c0c0c0c0f83b9400071196a9129b7068404523e2047ef79f4ed0dec0e1a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0ee94000714bad5097985304ae187a64c80bd07e8694dc0c0d4d381dd900000000000000000926320eabb0ea745c0c0f8a994000f3df6d732807ef1319fb7b8bb8522d0beac02f88ef845a00000000000000000000000000000000000000000000000000000000000001601e3e280a000000000000000000000000000000000000000000000000000000000688a1197f845a00000000000000000000000000000000000000000000000000000000000003600e3e280a043382fa828ccb8ae9633e36a492715ec664985ebeccd035eadf056d1b39677adc0c0c0c0f9045d9400253582b2a3fe112feec532221d9708c64cefabc0f90441a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdaba00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdaca00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdada00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdaea00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdafa00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb0a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb1a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb2a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb3a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb4a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb5a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb6a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb7a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb8a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdb9a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbaa00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbba00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbca00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbda00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbea00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdbfa00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdc0a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdc1a00613e1309f1f4a675a311b2c378db255be0cf9c2d1154148fd1c6970f848bdc2a02761937fb52f3d1f6912a62e68bf2e7d1aff8a31e98b1b4af384509032186e7ba0c1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bda0c1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bea0d62134cece6a5228d4d7908227d8d5290aebe1688166fd47933dd5759abe3ddda0d62134cece6a5228d4d7908227d8d5290aebe1688166fd47933dd5759abe3ddea0d62134cece6a5228d4d7908227d8d5290aebe1688166fd47933dd5759abe3ddfa0d62134cece6a5228d4d7908227d8d5290aebe1688166fd47933dd5759abe3de2a0f714bc55ab500649c6fa224622306976f190fc0fd0f1713757fe954b1300af28a0f7e80f70c93aa356ac8612f343ec2951a94e0deae2dc0637eb52c7e05fd61ffac0c0c0f9012e9400869e8e2e0343edd11314e6ccb0d78d51547ee5f88ef845a02230d0ae1ee7ae79b8f3624e29ce1ebc0f74f53d3535fda30a2c2b5dcac7ee6ae3e21da000000000000000000000000000000000000000000000553be9b0bafd81e5e5caf845a03219a028fd7c2a1b908842a1d51cc4bacae5742282b971d6f1f91ab2d5f302e8e3e21da000000000000000000000000000000000000000000037fead2fcc74dcf04ba13bf884a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a058524eb6fc976a5072c9b12b3161d93b26c1dc1831012e447c4eae9abc63fdfdc0c0c0f901de94009c5b7ff119972e3437b51c4f94addb8dbb2bcdf9011cf845a08be2a7121582abe077530ad3c62f5f9b078bab9fc2ef2a83498c509bdbba8380e3e266a00000000000000000000000000000000000000000000000000000000000000001f845a0aa04e8c3bc33696b6de2dd8ca030700a124a634ce36389b0709be2297f814659e3e266a00000000000000000000000000000000000000000000000000000000000000000f845a0ca3ddf8200123e447b2fa02fd7902b43b3f4f836796f6cab49d15a1c074ee98be3e266a00000000000000000000000000000000000000000000000000000000000000001f845a0edb88eb6887722cedc23aeb78c448fd1ff70bcf56242999080473a6ebd466abee3e266a00000000000000000000000000000000000000000000000000000000000000000f8a5a025f533f5816fea98721b07c867512a09046bce89bbaef0fc894d3d6ef5121f89a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca038ab998390c0f0d40334ccd731dc9574d7eb8711b196e55a07122eb0cdf43e8da0c6c9b674dde25306a588253a1b458bcf70ced5cec4f3a95648b34af02e1be1aaa0ec042f961d17328f4e0f726775c0ee755b3072a4d82a7244ea5a7a0c683795efc0c0c0da9400c600b30fb0400701010f4b080409018b9006e0c0c0c0c0c0f09400ffb0a36fba9ba77ccfa8be56972ddcced05820c0c0d3d20f90000000000000000001e40d4fd3e18287c3c20f59c0f84c94012259c7510e65b3acad37cbb4fd67eaac24937cc0c0e6d25690000000000000000954c3ca972e77f184d25790000000000000000954c2abe581fbb2ccccc5568305d0a2c5578305d0a3c0f094018bb2f454385bc878d802685ae674c90d95444cc0c0d3d276900000000000000000000023bce5fce980c3c27604c0da9401dcb88678aedd0c4cc9552b20f4718550250574c0c0c0c0c0f09402215a21eb83e959522158aedc776a0dc995ee5ac0c0d3d263900000000000000000000873971cadb5adc3c26303c0da94025106374196586e8bc91ee8818dd7b0efd2b78bc0c0c0c0c0f09402e376b6d590680d99dc422be8ce718730e15d50c0c0d3d24a90000000000000000000074516d2822081c3c24a08c0f09403176009748a757985c79f852498c279cf6d24e9c0c0d3d259900000000000000000000750326782c4fcc3c2590bc0f294046db6d00ad3ba82966b6f79099d49c5e3e5fb0bc0c0d4d381aa900000000000000000d7c8ee5411a74e47c4c381aa1dc0f9026e94050362ab1072cb2ce74d74770e22a3203ad04ee5f901cdf8afa00000000000000000000000000000000000000000000000000000000000000035f88ce25aa0000000000000000000000000000000000000000000000000ea65558350590000e25ba0000000000000000000000000000000000000000000000000eb16f83f7f1e0000e25ca0000000000000000000000000000000000000000000000000ebc89afbade30000e25da0000000000000000000000000000000000000000000000000ec7a3db7dca80000f845a0210b973bd7f4c7abc9f7e83edcd2c13ee790c598665119fb3e22b41abb2ada2ee3e25aa00000000000000000000000000000000000000000000000003997c30329df0000f845a0970096429554d9b710fc3248002defd83d960e12966bca99048d04b5ef9a98ece3e25ba00000000000000000000000000000000000000000000000003810f6985c940000f845a0a466451530d5054781a968d8eb1f4bb39149ce649fff0a5c6f4102ba1b3113a9e3e25da00000000000000000000000000000000000000000000000003a9073a438260000f845a0f997360e2ba32811e5839df15d4bef8dd6d5f7966f9430fb6d9c26aa2b5de66fe3e25ca0000000000000000000000000000000000000000000000000404110781e0f0000f884a0000000000000000000000000000000000000000000000000000000000000012da0000000000000000000000000000000000000000000000000000000000000015fa03b1c0a3df7a3e29a3f093a0d57306f7741c6da12b68b39213eb87923f236dc12a0a3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50c0c0c0f294054ae108de8f68dab97b8508bacd6a915381b14cc0c0d3d27b9000000000000000000000000000000000c5c47b82021ac0f90108940591926d5d3b9cc48ae6efb8db68025ddc3adfa5f847f845a00000000000000000000000000000000000000000000000000000000000000005e3e230a00000000000000000000000000000000000000000000000000000000000000851f8a5a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004a04a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8a0528a00ed2fb81b8539ca83d896e99a8b2447d5745188ae265814adcc5d59111ca0d2c3c16adb7c0c03ff3dacd2242c2e2532daa71938073ae5631ae9b5ed200fb7c0c0c0ef94061be0bc209a27588b7e85c0032ac8a0cdfd12a5c0c0d5d482011190000000000000000001789c3bf3c0d622c0c0f294065ac3d33fec104fba9f2f4d674afaa7c4ebcf43c0c0d3d23290000000000000000101815b9db36ad3e3c5c432827792c0f29406a4c42134cf1dca553112f71249dac3b9862242c0c0d3d2269000000000000000000e9f9c849ccb0f3dc5c4268203edc0ed9406d9de307b550b54acaf8a654322da29f45cfe05c0c0d3d257900000000000000000000102b674ed31f9c0c0f49406e0e19d02025dd6d9b5d6dde8109d2e7456f8f9c0c0d5d4820111900000000000000000000028b74a532d92c5c482011119c0f094075288e6dbea91c969cb1c93a425573c24c4093ac0c0d3d2559000000000000000000001f4d813521a0ac3c25549c0f83b9407a5739ab2f4590f1909ed5fa110a92ef940fd70c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f09407cb86bbe058cf36534f2f723e4b374b97723622c0c0d3d2189000000000000000000005f1ca9899c28ec3c21802c0f9012e94085780639cc2cacd35e474e71f4d000e2405d8f6f88ef845a07f084301dccacb1b31603f885ad6fa465363b06dca39d826357104bd038d9ca2e3e251a0000000000000000000000000000000000000000000009010b47b07a0c7ad20a6f845a089bcc199ac17a3d0b6a47a9f375743e8c6409684396005aa50bd459b3d4db4afe3e251a00000000000000000000000000000000000000000000cef77fe89dec6d5c58345f884a02bcae90d8019b744a79f5fe0371b29d49746ee29e5dd653f2bdf45154fee95b7a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0641a0b1cce6113928b4c3514efbfa57e40e5ebbcfd4cba8e76085e20913b9429a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f90133940a39d62bbb4976ae2e4184dc365e81fb73356b5cf8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e236a0000100000100010000fcf9e00000000000000000000342d58d71716ed4c78e97f845a00000000000000000000000000000000000000000000000000000000000000001e3e236a0000000000000000000000000000001c98a70671d00653e164f89f7d6bbc6bbe3f845a00000000000000000000000000000000000000000000000000000000000000008e3e236a00100000000000000000000002807c294a8468df98efffffc0bb16d10688a1197f842a00000000000000000000000000000000000000000000000000000000000000004a0d8a47cc0a98b326a7d41ebdba69f2440a3db7a31c7edca3bc5f01523d2fabe24c0c0c0f8e9940a992d191deec32afe36203ad87d7d289a738f81f849f847a064e1acb13eeb851346a80b7c1d1f82fe74310c98872564af7bdbafd45a27e559e5e482011ca00000000000000000000000000000000000000000000000000000000000000001f884a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000004a0e02b1e83e1e04adbc50a55e8ca8dcb102afb9a3d974fcc42100380e5f67d8b30a0ee01226252351611ebf84fc893d9de7477a97e335dd65a37f0ada02ea12ffce3c0c0c0f3940ab3fbc9025ece0ea4e0f9d29fbaa94b70923e37c0c0d3d26c900000000000000006212a96e30568db5ec6c56c83012a27c0f863940b2fa18342d38909e3e44d7103cf3f37b0a0403af848f846a06f3504a2e281620f68052ebb2f627e08c0bcbc440d737a4153561a82f2ad80cbe4e381cca00000000000000000000000000000000000000000000000000011abb81f05d296c0c0c0c0f2940ba0a5f4425129dc261d5323e9c0066fcd2e24d0c0c0d3d22490000000000000000002aa1dcacf3366ddc5c42482074ac0f87e940ba6d224169bbfad4eb2d38c500d2c8e8ede696cc0f863a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000004c0c0c0f90166940c1df9ddd357c326b410f60904242cd8917bb931c0f9014aa00000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000ea013f7507e64d7986f1516239122fd35a72de0baad7e5bb26e6e6b23e8984ca8baa03817baa969faf1767c244f2ade78fa0b80530927b15a252978a418205db88ceea040fcb98b1130c08bba5f040aad7cf7f223963a65ccb9e2e5ca7f16237836eb05a06d7dfbb9a4112df2130eae7564aef3bc18a2fc1beb290b0df160fcdbec51645da08a0b52ec4a28516cc30451632938dcb471f4ab15f9b45401ea30fe063ce583f3a09ff29f90a7de0646e6a8702b3cfbd32219dcd7e154abdb806954853bdb997faca0d33a68369d4ef6824db0497f69f2488da5c3eedac6cd7a3d84497f5d6aed69c8a0f942f4688cdba65adc8aa59da583acae93fa87351143ebc775559218bfa5f832c0c0c0f87e940d5f4aadf3fde31bbb55db5f42c080f18ad54df5c0f863a0000000000000000000000000000000000000000000000000000000000000000ba0c6e53a41f830843dc3dcf4e311f022c8cb332183ad4f7d1997a8b38406719084a0c72832eeeb6c954f2d21e989302b29df1947780c65be799f4fd90fe3c616336bc0c0c0f83b940dd0489bf03f0e31489ea33114ab261959c89986c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f2940dff7526ebe29be4553d1f5817d8257da3f2579fc0c0d4d38197900000000000000000000131c2ae2a225ac4c3819702c0ef940e331a293bb22e93e2e5e417d84d755ae185da02c0c0d5d4820142900000000000000006c58ee435813c1d3fc0c0f898940e87bf5286c4091e0eeb7814d802115dfbb4c4cdf848f846a025b5e2a05b4193c39534625acd7c87082dcea62b0cc5a23f5c4d85cbc89f5dffe4e381b0a0000000000000000000000000000000000000000000000000000000688a1c2301e1a00000000000000000000000000000000000000000000000000000000000000002d4d381b0900000000000000001cf7cf20fcb14ead1c0c0f2940e8cf9f54fe94c31331b1a147cdc5dd81cfab549c0c0d4d381e19000000000000000000001518fb762f894c4c381e102c0f5940e9e39190df7a2d38872736910cddfa519e419cac0c0d4d381e590000000000000000665fef590e3cf450bc7c681e583075aa7c0ef940f7936a55a6c29c4c344b50acea925142b092a1cc0c0d5d4820117900000000000000000000221b262dd8000c0c0f901549410f9b71043ff1c882dd7b928e724935b9dcca9ddf8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e25ea0688a119700000000000009577b1f00962acf000000000000132ec8981331331df845a00000000000000000000000000000000000000000000000000000000000000009e3e25ea00000000000000000000000000000001941a2d04687f462dabd5af793445a25c4f845a0000000000000000000000000000000000000000000000000000000000000000ae3e25ea00000000000000000000000000000014262b427a98cbe305c52b58fdff50b9894f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0da941103c0550771ffb586688ffbba90c6b85ebfc224c0c0c0c0c0f88394111111125421ca6dc452d289314280a0f8842a65f847f845a0b60746c954665f59975784c796b478be7bf99adc1977555d15b50bf6290dacd5e3e265a00000000000000000000000000000000000000000000000000000000000000001e1a00000000000000000000000000000000000000000000000000000000000000003c0c0c0da941111111254eeb25477b68fb85ed929f73a960582c0c0c0c0c0f9015a9411b815efb8f581194ae79006d24e0d814b7697f6f8dbf847a00000000000000000000000000000000000000000000000000000000000000000e5e482013ca00001000190019000e1fd0a52000000000000000000040674d11d82761bcc0f3ff847a00000000000000000000000000000000000000000000000000000000000000001e5e482013ca000000000000000000000000000001b75688b1a5df1f39b981ff71e64e1963da9f847a000000000000000000000000000000000000000000000000000000000000000e9e5e482013ca0010000000000000009cd0413e671a73798ef47b227ffe7d68bc8d0b1688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000000e8a02f2606b2c0d121a5cc1b59088ba7234e9d1c805f41724c938a2661d69532e0e9c0c0c0f90124941231deb6f5749ef6ce6943a275a1d3e7486f4eaec0f90108a00bec691802661a606c8bc6fe9577f8e94849b97a5ffd094963f0c77c0585626ca031f5357de7a47a7ed3a6810af415c7ed66aeb4651d07848ead0606cdba9c8a16a057a1b6793381655140bd623273394b8dab82adb402765f3433b81ecf035e4a2da0a65bb2f450488ab0858c00edc14abc5297769bf42adb48cfb77752890e8b697ba0b269973686955134850c5973c540ca3061d6a326964b2224ddbc00023a045092a0b3f3361cb646afaced8225648f30eec402f4d7cd20ae3ceca8ad57b39fc3391ea0d4b09808ca0bb39d4433b29d05786b45ea1ff016726a2b747acb136db277e42ea0e316ae1a88fdf2c09ee13c9e4dfa09930537e6530cd8f8929c42b22bdd863f69c0c0c0f49412825cdba96a46fbc7d3e3fba2c9ff3dd84d09b8c0c0d5d4820136900000000000000000000000c2cb0de1a0c5c482013606c0da94136f1efcc3f8f88516b9e94110d56fdbfb1778d1c0c0c0c0c0f494138d2da478ae9873c8185bafc17d9e5da9a0598dc0c0d5d482013590000000000000000000b02d1ac5e49641c5c482013503c0ee9413e1ec44037bf6764950777b8d2b1b26b99632f6c0c0d4d381d9900000000000000003afacfa39d1c98000c0c0f29413e9363758de918ee531482a7b19e615aeede423c0c0d3d237900000000000000000006ef7edc46bb630c5c437820fddc0f83b9413f4ea83d0bd40e75c8222255bc855a974568dd4c0e1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0f85d94156f73fc73197555e950743cb2b23f411c751002c0f842a0185e45d88af45849d78c989b76920d90583eaa2278e64a5aae6329f2ef0dee51a0834458dc7352fae924833cb7598fe0247c42914818a279b8ce4e2c01039b7a80c0c0c0da9416623c35ef61d92aaaf98f25bd64ea962fbd6a12c0c0c0c0c0f901579416ac64ddfacca437be7fe0da0c45bcd97227588af8d8f846a00000000000000000000000000000000000000000000000000000000000000008e4e381fca0688a11970000000000045c73e2cfc593365d0000000000000005f64ab89cd250f846a00000000000000000000000000000000000000000000000000000000000000009e4e381fca0000000000000000000000000020be0ffbacdfa2c2765fe6b22b9ebd04e77f94cf846a0000000000000000000000000000000000000000000000000000000000000000ae4e381fca00000000000000000000000000000000bc0b06045763b2c6afe45b7e64204b0b8f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0ef9416beb8782c2607fd90ca93f3119cf12c00255c6ec0c0d5d48201479000000000000000444d78be7343619026c0c0ed9418342abc7cea61c04889f145c89b584a2ca93778c0c0d3d20a90000000000000000000118928417e22c6c0c0f85d9418577f0f4a0b2ee6f4048db51c7acd8699f97db8c0f842a0000000000000000000000000000000000000000000000000000000000000003aa0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f901fd94186f04af76ccf1a99f86b49175e89960903de747f8d8f846a06783cf743dec8bee953a966099d2b43454e44d609db101887fb8b2aa3c76c8a9e4e38183a000000000000000000000000000000000000000000000000000006ec7542acfd6f846a08ac150a06d1303d7b276beccd2df131d9360ab0036a5e5094aa0dc068c8b821be4e38183a00000000000000000000000000000000000000000000000000017d8cab133ddeaf846a0d89359e15bcd91e83fcf5d0a4b0acf703026fecff85245f9ce2baca63b0aab7ae4e38183a0000000000000000000000000000000000000000000000000015ff5f4c7e657b6f90108a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da037836a7135fae77e265e35732c70286035736c8b57b12590769780e067ead81ca09eb9dbac5972920082cba1eea1e2792b8033f91c80f7f1b245e77d3ef4fcb68ca0c8092037aa878ba2113bb8a8fd8f2ff609bea97a87d4e80ca61a16f0ca18dd99a0ce0fbc8cb1df34eb23ee46d9cf16e1a93241e42344197171bd07612b0bde7491c0c0c0ee941876d76b77ddf37a78a073aa35131bd5f58f19fdc0c0d4d3819f90000000000000000000016bcc41e90000c0c0f29418e296053cbdf986196903e889b7dca7a73882f6c0c0d3d231900000000000000002157410a215fb36b5c5c43182a2cec0f8ca9418efe565a5373f430e2f809b97de30335b3ad96af88ef845a00000000000000000000000000000000000000000000000000000000000000036e3e208a0000000000000000000000000000000000000000000431c1b69e12ab108b99cabf845a094d7c397f910069f86ea2de900e1f4e266063e4394049dd244977c755e42c4a5e3e208a00000000003462ad45178006aa8f3ad0600000000001503436cfb7c4b872d1eb9e1a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f094195d09fcba4ac3f93898e51f6edaa7fbf1f8c0aec0c0d3d24d90000000000000000000074516d2822081c3c24d05c0f294197bced376e0dc12c00dcc19b43d2afd9b8533c8c0c0d3d2789000000000000000000fad652b70acc705c5c478820994c0f90102941a7e4e63778b4f12a199c062f3efdd288afcbce8c0f8e7a00000000000000000000000000000000000000000000000000000000000000035a003cc2d281da0ffdaa3b9283c2e5b394b074a5ededae910f6bb6e3a4d11a010c5a01259640b4adb4af8dfa1fb1e61fcbedd02b012e121c8b41224ae42e6df833153a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca05fedb3d67ddedda3418dd7feb9217a7583ec41bc24951ec14e72901d3624b9bea093870757cb31b01a453ca904251af33e85caabe2631d491839de8262007e362da0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f854941ab4973a48dc892cd9971ece8e01dcc7688f8f23c0c0ead48201149000000000000002809b0b354bd69bb549d48201159000000000000002809b0a9d8f26c1bca6d0c7820114831bd300c7820115831bd301c0f902e2941abaea1f7c830bd89acc67ec4af516284b1bc33cf8d6f846a08c40cf33b9ceba87302dac0dad21186d7b361192fb3f09d1b96fc4f94580601ee4e381ada0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845a0dd21eaa2a7b91d700e186a088ec24a6ffae451cbb867f014da63e9784f6a5049e3e210a0000000000000000000000000000000000000000000000000000000000098d8fef845a0e9a042b510c4a11346b2bb8943779a6be59156b3193c52e4e1d3b52c400ec46fe3e210a0000000000000000000000000000000000000000000000000000001d71dcada73f901efa00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000006a010d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390ba0115839bfee55578761c7edec3b2ff273385ed4d5df39b75ab78870ff966c69b8a01f1a80db9a82d9fd4ea3322b7d2dcad52fb678229d487c4bb44e821a2544709fa024bd6c6f7d1eeabad95a250d5ab0bbeaae9c7223e36ba7b50aea221573ce7122a02b37bb377e3251959b71e36eb5119bc1a95c91cb6bc962484ad3386a13b4db4da040ef085811c240d7d326b31f33d2ba08796489e37cb6fc10c7a3ea547d92cba0a045dd9f8e28a0592fd98e277ec8668c9664b6acfea9def7dcae17c6bf9c297112a07050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3a09bdc0f5dbc7c5ca99391aff6b41dba4ce516f1bcb64a70dc8a8338bf35d08e49a0acfa85f8dca23f171b19bf4838ca8f35894504326d0e8354507c4fd5f1a60701a0cf842f22bd921fa3f6c476d873397f10ff82548909c51ec0b56f889a142139dfa0de2897c64f0af56474cba6e3bb2d58b5c85a42ee6dff042546bc919c03512137a0ef83a7fd694bd220a3a3d2f147c273844b8326010a4cfff2bc72b6665aeb6b13c0c0c0f901de941ac1a8feaaea1900c4166deeed0c11cc10669d36f9011cf845a00000000000000000000000000000000000000000000000000000000000000000e3e207a0000000000b000b000302f5b20000000000003f9d9d5f88995fd963b9687a79f8f845a00000000000000000000000000000000000000000000000000000000000000002e3e207a00000000000000000000000000000000000037373281c28b3736d9f0a73bd212bf845a00000000000000000000000000000000000000000000000000000000000000004e3e207a000000000000000000095ba901291763e00000000000000000000000009760941f845a0000000000000000000000000000000000000000000000000000000000000000ce3e207a00100000000000001e449e9956ad20d18fb4328a5b2000d4256f7a0e8688a1197f8a5a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000010008a07a74ceeb7e4916057732409db262d7d77bc2c68592f515eadf744fc0b19727d3c0c0c0ee941b715ddc19e3738251f91ea21e9c3eb702f5c9b0c0c0d4d3818b9000000000000000000ab70821a716ac37c0c0f2941ba114a6cde4fb1f65039cfeba13eaca7fda67ccc0c0d3d25290000000000000000000557d9614b5c0ccc5c452820180c0f2941ce998946b00b61a00f69a77049aae108c408fcbc0c0d4d381ed90000000000000000000012c36c7310690c4c381ed06c0f83b941e0049783f008a0085193e00003d00cd54003c71c0e1a058d378c017b147e20c5e2165d42ae5632b3439c6aa34a0ecc35c5db91bbf26a4c0c0c0ee941e4258d9a285d3382c21b5555239468defb91d6ec0c0d4d381f7900000000000000000001ff973cafa8000c0c0f83b941e9297c3c1bd669f86522763f16b0e4ebf673b21c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0ef941ede399f9ecbca800a2655f4274f3df535ed7123c0c0d5d4820146900000000000000000107c6170c3920a53c0c0ed941f47f15fdbc9b9ba5b72e90c3ee0324db5cb09fac0c0d3d268900000000000000000000aea082ab80b49c0c0f8ee941f9840a85d5af5bf1d1762f925bdaddc4201f984f890f846a0ace61e29be7067193354e9e9063ec5a13634b45c5589e1cc0dc901f3b7f30287e4e381caa00000000000000000000000000000000000000000000022180c2bacf589db1800f846a0b11b2b2fb103174cd512ae891e4756b7aab3e7f6f806bd0a18eb436b3174f84be4e381caa000000000000000000000000000000000000000000000000000000000001c0000f842a008cba8c407b93d4ad104b6aed9467903dc943b52ce23fabf876bf1ad44e0d3b4a09de5f436cd59c5c3a345cf320707d5a259457b4cc5665c19201d1db59b103a6dc0c0c0f90111941fc122fe8b6fa6b8598799baf687539b5d3b2783f8d5f845a02422a63740c835eaf609f2253a3d68f4f962e5e3700be653757bffb7511df3ace3e26fa0000000000000000000000000000000000000007e37be2022c0914af5e7701080f845a0350c7aac05939e3aa7c17dfd2940b6a28728d4d22476d6d9b7bbcadc72c16d67e3e26fa000000000000000000000000000000000000000000000000000000000000ed30bf845a0b90fd484c7fdab79d88527e78135ea54d0fd198d20d41ea87e625a17346712aae3e26fa0000000000000000000000000000000000000000000000000000003dca1285520e1a00000000000000000000000000000000000000000000000000000000000000005c0c0c0f901579420ce9219b423ab30468ddb5fde57ab23dc2a7c65f8d8f846a00000000000000000000000000000000000000000000000000000000000000008e4e381e9a0688a1197000000000000b5ee1c2237dc7145000000a712c2d2f54d7f7551d065f846a00000000000000000000000000000000000000000000000000000000000000009e4e381e9a000000000000000000000000000000000000000470d848b898bfb80cfb0c92c18f846a0000000000000000000000000000000000000000000000000000000000000000ae4e381e9a00000000000000000000000000016693c17c40b1239af4313853891f010ce9aacf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f86c9421a31ee1afc51d94c2efccaa2092ad1028285549c0c0f83cd381cd9000000000000007b0ca206c1555136178d381ce9000000000000007b0ca1ffa7aabda8ce1d381cf9000000000000007b0ca1805aac9431789d5c681cd83bfdde1c681ce83bfdde2c681cf83bfdde3c0f9064c94222222fd79264bbe280b4986f6fefbc3524d0137c0f90630a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df77a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df78a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df79a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7aa00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7ba00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7ca00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7da00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7ea00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df7fa00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df80a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df81a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df82a00b4728f31968907413766ead4408f0d695318c988a8369b4fc85a7183816df83a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df4a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df5a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df6a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df7a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df8a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214df9a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dfaa049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dfba049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dfca049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dfda049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dfea049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214dffa049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e00a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e01a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e02a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e03a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e04a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e05a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e06a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e07a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e08a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e09a049c934932e9be834913ca97d2d2d63ba4f81ca11e1c1a0fa922b5b1199214e0aa074bbe6d0cc07a4b7d1ec352e4461c00af830e486fe31a0ab5a772b85a189628ea074bbe6d0cc07a4b7d1ec352e4461c00af830e486fe31a0ab5a772b85a189628fa097f94e1b6f1f73e598ab5fd3eb79c7b5cb0599a73b01d3883739b7c31585aee0a097f94e1b6f1f73e598ab5fd3eb79c7b5cb0599a73b01d3883739b7c31585aee3a097f94e1b6f1f73e598ab5fd3eb79c7b5cb0599a73b01d3883739b7c31585aee4a097f94e1b6f1f73e598ab5fd3eb79c7b5cb0599a73b01d3883739b7c31585aee5a0aff9bc33f5c50fb76b881dfb0c57123bf484a2c7240cee706070de06f0a1cfaaa0c1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bda0c1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bea0c1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bfa0f714bc55ab500649c6fa224622306976f190fc0fd0f1713757fe954b1300af28a0fd48a94d0d1a2d046b04cee0722784378c199fc6dea8c8f8bdca5dfcdf9a3cf6c0c0c0f09422243827a8df1aa885dab5ee7687797ea81e41c7c0c0d3d25190000000000000000001dd0e22b67eafa5c3c25151c0f90159942260fac5e5542a773aa44fbcfedf7c193bc2c599f9011cf845a045f0b7fa21a4287c5918668b6ede365097a5a85bd0ca3a042787e67ede5e18b4e3e20ea0000000000000000000000000000000000000000000000000000000000286cdf0f845a069a1997d5f27fd969be349ea22f3e24ef4e94682f22fa7a1b6e23f01fc312c59e3e20ea0000000000000000000000000000000000000000000000000000000000000001af845a0886009abd6dd5151b65fee0191dd9d9bd486b40a87d629d2d1d4fd72013f2859e3e20ea0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf6ae7f845a0dc276a4f120117ad5ae6415d1c724b4f3a0e81f0ee6466e1392ca121b63123f2e3e20ea000000000000000000000000000000000000000000000000000000000d37ac93ae1a00000000000000000000000000000000000000000000000000000000000000005c0c0c0f88394231b0ee14048e9dccd1d247744d114a4eb5e8e63f847f845a02b1af9e4b5950876f69e3833c3939f3309f28bd37d533255756b953403fa2291e3e239a0353636372e686f6c65722e65746800000000000000000000000000000000001ce1a01b7bdf7aaaf4a7bf4a4756f270ee99d9e9b6223012c1ac9bc423435181210951c0c0c0f49423214fc64f1b609ff79e3ebb666ff892e2c0ca40c0c0d5d482012d90000000000000000000000ae6e57aff18c5c482012d2dc0f79423f4569002a5a07f0ecf688142eeb6bcd883eef8c0c0d5d482013190000000000000000698df6a5cd76d93f8c8c7820131830a7826c0f9030c94240d6faf8c3b1a7394e371792a3bf9d28dd65515f90120f846a0000000000000000000000000000000000000000000000000000000000000000ee4e381fca00000000000000000000000000000000000000000000000000000000000008770f846a05d0b89f1b51e963958b73fa8b0835e08bfdaecca196291273f337d25b48d0ab3e4e381fca0000000000000000000000000000000000000000000000000000000f934085867f846a086dc9f48f922470d891ad8166fd17c619a5348d23af6c21756b135fb49eb5ac6e4e381fca00000000000000000000000000000000000000000000000000002748f74e45858f846a089f5b3833a9bc58098227bec834ea97ac15be9fb10bb4ab19443b191c76f42b5e4e381fca00000000000000000000000000000000000000000000000000005f64ab89cd250f901cea00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000014a02c681bc5e3516f0bb262f170d216d88300ee524f00f4e27e476716d2242a9e49a0a16803f2d86ce308ef835139aa4ab43f190fbf8b1d2d71cb9f8475167e35c40ba0b4c2b7416d73f8e278a67a96d8dee301381a96139e7d1e6430e319f9b0eb43d6a0cee0c0d05f224c5801a59822161796562458a631ec31c0bbe4f7b20cad8f8a7da0ee61682ec263cf9ac626d5a9b0cd5e26edbfa98b5b368b540e65bd739c37e3b7a0f617aed219efc795cc0cd455bc8c1b56c59215667268ccc45369199554342a26c0c0c0f494245287bb1afafac630a0e478872ec1fef4048bb8c0c0d5d4820105900000000000000000000af93b5d92c51dc5c482010546c0f4942458e701b1b51911540bf8259f112bcef43b3b0cc0c0d4d381e390000000000000000000f283f06160e9e9c6c581e38201aac0f85d942486ff197be4553775daf666a815deac482bb6b0c0f842a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000003c0c0c0ee9424c62a454eed391d99b152299adf160792417489c0c0d4d381fb90000000000000000000057f249641d400c0c0f86394253553366da8546fc250f225fe3d25d0c782303bf848f846a00e8c03cdc070ac416f451e06ce792af4fd5ef7708902542b2828cd0910b109fae4e38191a000000000000000000000000000000000000000000000000000000000688a1197c0c0c0c0f694258494a21d9ea90fcbcb9e22bd57c6899de0d995c0c0d5d48201229000000000000000005eace9819926d664c7c682012282cfb6c0f29425ac9c9a876e9739a035036d87e7e64c7f3f42f5c0c0d3d25a90000000000000000001f5e8c122db6ab4c5c45a82042bc0f19425d4c9c8abf1fa541f194fd93e4e3cac07e509e8c0c0d3d20b900000000000000000048ebffba735c80fc4c30b81fbc0f49425ea2f3efd7fd87b830c993e55e1fb6066c753b1c0c0d5d4820109900000000000000000001203178dc2e612c5c48201091fc0f294260b364fe0d3d37e6fd3cda0fa50926a06c54ceac0c0d3d220900000000000000001175975114efd9eb8c5c42082392ec0f494265867026965298ce9579b1a04899454ebfb12e2c0c0d4d3819690000000000000000001fb9827009542bbc6c581968201bac0ef9426ab2ebe1a3bd8c6b752e9d5765728f8e5706496c0c0d5d482012f90000000000000000003f7ed4932ae6000c0c0f930dd9426d85a13212433fe6a8381969c2b0db390a0b0aef92f54f9066da00000000000000000000000000000000000000000000000000000000000000008f90649e218a0000000000000000000000000000000000000000000000000000000000004d277e23ca0000000000000000000000000000000000000000000000000000000000004d278e23da0000000000000000000000000000000000000000000000000000000000004d279e240a0000000000000000000000000000000000000000000000000000000000004d27ae241a0000000000000000000000000000000000000000000000000000000000004d27be242a0000000000000000000000000000000000000000000000000000000000004d27ce243a0000000000000000000000000000000000000000000000000000000000004d27de244a0000000000000000000000000000000000000000000000000000000000004d27ee245a0000000000000000000000000000000000000000000000000000000000004d27fe246a0000000000000000000000000000000000000000000000000000000000004d280e247a0000000000000000000000000000000000000000000000000000000000004d281e248a0000000000000000000000000000000000000000000000000000000000004d282e249a0000000000000000000000000000000000000000000000000000000000004d283e24aa0000000000000000000000000000000000000000000000000000000000004d284e24ba0000000000000000000000000000000000000000000000000000000000004d285e24ca0000000000000000000000000000000000000000000000000000000000004d286e24da0000000000000000000000000000000000000000000000000000000000004d287e24ea0000000000000000000000000000000000000000000000000000000000004d288e24fa0000000000000000000000000000000000000000000000000000000000004d289e250a0000000000000000000000000000000000000000000000000000000000004d28ae259a0000000000000000000000000000000000000000000000000000000000004d28be27ea0000000000000000000000000000000000000000000000000000000000004d28ce27fa0000000000000000000000000000000000000000000000000000000000004d28de381aea0000000000000000000000000000000000000000000000000000000000004d28ee381bea0000000000000000000000000000000000000000000000000000000000004d28fe381c0a0000000000000000000000000000000000000000000000000000000000004d290e381c2a0000000000000000000000000000000000000000000000000000000000004d291e381c4a0000000000000000000000000000000000000000000000000000000000004d292e381c6a0000000000000000000000000000000000000000000000000000000000004d293e381c8a0000000000000000000000000000000000000000000000000000000000004d294e381d1a0000000000000000000000000000000000000000000000000000000000004d295e381e4a0000000000000000000000000000000000000000000000000000000000004d296e381f8a0000000000000000000000000000000000000000000000000000000000004d297e482010ca0000000000000000000000000000000000000000000000000000000000004d298e482010fa0000000000000000000000000000000000000000000000000000000000004d299e4820116a0000000000000000000000000000000000000000000000000000000000004d29ae482011aa0000000000000000000000000000000000000000000000000000000000004d29be482011da0000000000000000000000000000000000000000000000000000000000004d29ce482011ea0000000000000000000000000000000000000000000000000000000000004d29de482011fa0000000000000000000000000000000000000000000000000000000000004d29ee4820133a0000000000000000000000000000000000000000000000000000000000004d29fe4820134a0000000000000000000000000000000000000000000000000000000000004d2a0e4820139a0000000000000000000000000000000000000000000000000000000000004d2a1e4820143a0000000000000000000000000000000000000000000000000000000000004d2a2e4820144a0000000000000000000000000000000000000000000000000000000000004d2a3f845a001c7b51d269b3bd26cfe3377d5788f5d8a124597ac0f4320e4acdbbb4490d217e3e23ca00000000000000000000000000000000000000000000000000000000000000001f845a0023563ddca61babfa9367b24f7a5af97c80644872dc32f7e9a7dfe2c723ecfd9e3e24ba0000000000000000000000000a869c2b2546945b17fbcac82127e4b50b7b46eecf846a002492cee0c19418cc18fe865a79c051c3b2891c5b5c7bc55a1fe2135837831a1e4e381c4a0000000000000000000000000b53b99ecdb5e3e6c5ba60c04292d225c766435d9f847a003d1ad6171cabfc9761d6784dcafc333b471a77be4c1e5257ac6a2e0bdfb7538e5e482010ca00000000000000000000000000000000000000000000000000000000000000001f847a0065cb5cf44f21c8e6dfae32d712d1429a9be301615e42ac25b4556ae4668585be5e482011ea00000000000000000000000000000000000000000000000000000000000000001f845a006d595c0c546a50bba19672cb41ee05f9fc9538406e77ee48e2779d97dc0b78be3e24fa00000000000000000000000000000000000000000000000000000000000000001f847a008182415916572173dda50f2307a8f00d9b271c6f8782d509d0bcbd0f6059c0de5e482011aa00000000000000000000000000000000000000000000000000000000000000001f847a008492a8bf060586aeb075f5bbf285763147d0a778b9d78d60f391082796bf4d0e5e482010ca00000000000000000000000000000000000000000000000000000000000000001f845a008b305075006aad97d950ef8c77a457b6523d3a177f3edf12a6e64ca403c4f02e3e24da00000000000000000000000000000000000000000000000000000000000000001f845a0099c6ef5aa6a4c1e9a3be59509c91076c05c3333924f9847b66254db7480e887e3e246a00000000000000000000000000000000000000000000000000000000000000001f846a009ac6f6a902ac97aebb793401ff773cc8604451550cb6fc3b9b974455427d333e4e381c8a00000000000000000000000000000000000000000000000000000000000000001f846a00a04f2bc9631644195e2ed103c16f2f02c70055febd326cbafb967bf3a51c293e4e381aea00000000000000000000000000000000000000000000000000000000000000001f845a00dcf590c3286f494129c8fdaafbf8ad4268a7b36bc228b34d654d2374cfc7daee3e24fa0000000000000000000000000d4b454f6f95d9c66b38383810de0fa8cb7c135fff846a00f6a8809a1d761e2412d5e5f45e104df6d5105f1522b690032c512f061f4717fe4e381f8a00000000000000000000000000000000000000000000000000000000000000001f845a00fadac1105f0ce6216f0a1e200989a175b54a9d690fe5ac5707ec95c6b6d1be5e3e246a00000000000000000000000000000000000000000000000000000000000000001f845a0109d419491277d6bbb523197641c1f7a1cb7219d37303c5170f3a7d59751561fe3e24aa00000000000000000000000000000000000000000000000000000000000000001f845a011217b655961bb5f7e38c069773ee88a15dec9883fa435226e9ec9a46e75e7e5e3e244a00000000000000000000000000000000000000000000000000000000000000001f845a012eab2cf36bae9871896d6cfd7fbadba2a3857778288e90b704f5cbfe81acfd3e3e240a0000000000000000000000000ca43f78dd8a9c8e96b53dea4e13bda504a0f8c4ff846a01733b0890ca89ef5f97dc3f2ac040040814182384890717cde4f09d440a3f492e4e381c2a00000000000000000000000000000000000000000000000000000000000000001f845a01853fe16d1f8e5db8a7f7ec8334ac7472bfd692aaae23cecd1bad6c019f64113e3e23ca00000000000000000000000000000000000000000000000000000000000000001f847a018b32fd875fa28caacc7d044ce63a24787a6efd776d016cc7260c355df8a510fe5e482010ca0000000000000000000000000571f7be0d706e84bfc902fb503f668f73d97636bf847a018c663f03ca11fdb12822c154443dee653baa6bd2b24d8e4637c213e7ff4b44fe5e4820133a00000000000000000000000007f06f9a74ed2cf670c8320d5e53c88442dafedbaf845a01acd654019360477df7401d794a8565481097e13d88975cc81f2ee8f44ec54b3e3e27fa00000000000000000000000008381f65f5ec4fdd94a5c033fa4261085fbd005cbf845a0228eabc0daa0eba8ed5ef4a0d407f2989d365cd161c8c84f9026da3d43c2ea6ee3e259a00000000000000000000000000000000000000000000000000000000000000001f845a0236c781ae80cbdbea0071847d4b5202a045e5c168b050c60e60207e6fd303424e3e27fa00000000000000000000000000000000000000000000000000000000000000001f846a02465b20f86e1184d1214573d40864ad0cd24d58534e934ebe368745dc2226019e4e381c8a00000000000000000000000000000000000000000000000000000000000000001f847a02b03b506f06affef971166caa8d710bc0dc885044b4403661e8e7a19af25e8e3e5e4820134a000000000000000000000000070fb47c51e443e3106625fbbe839b6b21aa9f09ff847a02b3c06387edb756b68e999d8e88763ffc3306f5940b36017f3ece5498637abd7e5e482011aa0000000000000000000000000ad35bbbd3e34939017a37dbfc0f3d402d41d014df845a02cd8f695e31bbea5a3db3b364e344e010f9a847e02db82698835caa09149df3be3e243a00000000000000000000000000000000000000000000000000000000000000001f847a02f6ffd495f8f24e366309e900e470251f878294070e83b18869657f55bbb1d8be5e482011da00000000000000000000000000000000000000000000000000000000000000001f846a02f9a1d308f6c10387b18684becf4935e0d9e2857f99c7dddda786781385289c0e4e381bea00000000000000000000000000000000000000000000000000000000000000001f845a030af0e0ae86e84df6970023463b34388b810d34891a9ee2e186e98ec6cce40b5e3e27ea00000000000000000000000000000000000000000000000000000000000000001f845a030cebc16b291bd6ef3a54ca9b0f28a10b3fbf829df58276b8389ecee3f7f2aaee3e203a0000000000000000000000000000000000000000000000000000000000000000af845a030d47318d78f71cb5858894134a221f1bdb98d5f406283eed18b8c8b8d43bd2be3e27ea00000000000000000000000000000000000000000000000000000000000000001f845a032ad1271aa2d1bb282101240690bc521311ad2a499e5c9d03eae089f085639e7e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f845a03416eb792adb4c0ed7811bae6e769a9e55d6427edcf64dc10e2c1c833f634664e3e240a00000000000000000000000000000000000000000000000000000000000000001f845a03603dad2df3696ebb289a869b3ebccad9f603d6464f7098d3f3afde43ddab8bde3e23da00000000000000000000000005f034589713d3a9f4c67a09b478b800db7764cd0f845a03947e3dc7b356dae8a706f4e10cd4aa2cce52bb278eaa71e6412dc719005b01ce3e242a00000000000000000000000000000000000000000000000000000000000000001f845a03a41204c118c8f63063ab63369abe305e8d692537864fd1949e4857ec7b57b03e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f845a03e554a85c54e69b073489460a209289d16f23192068666d576e93e2ff3205ab2e3e247a0000000000000000000000000b8667563548726af6732c799e066c27e3e1efe62f845a03fdc10bf04f71f30599979cce58a6b8b4e7a2b1cf06ddeccaa6ab46cadbee832e3e24ea00000000000000000000000000000000000000000000000000000000000000001f845a042ad5ab90b088f605921428816a1d7a6adc7615d131e976b1e5732cfb8f0bc73e3e23da00000000000000000000000000000000000000000000000000000000000000001f847a043213d897f76d3e4023c39b2dbf73860e759affeb9315e8a508c6fbbdade1a70e5e482011fa00000000000000000000000000000000000000000000000000000000000000001f845a04648affe48e39f928027131793b67940650406cd37857db3f30efbde78bdc11ee3e241a00000000000000000000000000000000000000000000000000000000000000001f845a0465684ac534db0845cf39cd92cbf554d116524506ea702cbc698970497cb6df9e3e218a00000000000000000000000000000000000000000000000000000000000000001f847a04c9974627f731e1817656d8c44efff741fcf6a21c0fac235323cebfb1fba5b72e5e4820116a00000000000000000000000007aece52635da73195fc6163b6d250018e6b99c7cf847a04f73ebcab0dc5c7e1fcfeb5afcf35c9d49047dc89295395e242e783417935719e5e4820116a00000000000000000000000000000000000000000000000000000000000000001f845a056ced8fc63f7854f083af8af3f01193f92e58af4b047ca5782a59184ce5cb49fe3e259a000000000000000000000000003176009748a757985c79f852498c279cf6d24e9f846a0583f6234249df1a1e3e3ca949219cb66260297da47d4478c3342eb0d18fc8539e4e381c6a00000000000000000000000000000000000000000000000000000000000000001f845a05859ece2efb5e2f9d91571b8831ce326f79b66e84c4320963c164f5cb5b8778ce3e240a00000000000000000000000000000000000000000000000000000000000000001f845a05994a8fc5e1794df3ab93408749e5e72b9132ea80f0b999c2e4ecb16d1a2569be3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f846a059c62db97e61053849aa184abf25ebf9238dad3467c719adc4a1480db185b515e4e381aea00000000000000000000000000000000000000000000000000000000000000001f847a05bf144ca85003711f5cae46eb74a909bb316b0168ddc1b9311e5edb272f1266fe5e4820133a00000000000000000000000000000000000000000000000000000000000000001f847a05ce41ff757241aa6556286bd10a54ad0f612bda29014988b943257ec5b764568e5e482010fa00000000000000000000000000000000000000000000000000000000000000001f845a05e0ce3f0008ab6062d6c1d76192ff3d9f91408ee056571bd9d5ee01804d0b607e3e23da00000000000000000000000000000000000000000000000000000000000000001f845a05e814d91083df367d84dbb005ff9709145f5fd16780c061529b9cfbe1866df57e3e27fa00000000000000000000000000000000000000000000000000000000000000001f846a05ed702fc67f7a19252d2440997532741e5eb50fce6079c0dd004d17b0b76e0eee4e381c0a00000000000000000000000000000000000000000000000000000000000000001f846a05ee27e6432e6b3fae1fa182a6d29e8c8f4983b46be5fad50be06a11b8eb32d48e4e381bea00000000000000000000000000000000000000000000000000000000000000001f845a05f15c8aa9acdc7b4b861ebd26720a060692eca6fdc806879ae4995124b543923e3e249a00000000000000000000000000000000000000000000000000000000000000001f845a060307571513c92bf8f33862322318db5722bb4167386ca1ad200593b4d1bb32fe3e243a00000000000000000000000000000000000000000000000000000000000000001f845a06201495553e5dd1a61b45a4deb1414bf6f680fdc050d196e7f5e4b0e032e09d6e3e249a000000000000000000000000084d76430449a30e20805a52086f711912824340df847a0623fc8c6e538b81f77c85b30f805b3bab7dfab18ad92c661dbcf35d4962284d4e5e4820139a00000000000000000000000000000000000000000000000000000000000000001f845a062fdb39ec57501d95fc463138335ec1ec31c4f24e47800e432189683dd916618e3e24ba00000000000000000000000000000000000000000000000000000000000000001f847a063540e8c7256e7b968929b3a85e45c4f583a8e69b6e548bcd56bfb3ffdc04986e5e482011fa0000000000000000000000000998c061820903de9af6d950eddb978af328c8326f847a06549fcb34c2dfddc63340bd5d3e1c0413fa924e937ef17f46208a29021f3705ae5e4820143a00000000000000000000000000000000000000000000000000000000000000001f845a0686e0314c7fb25edcb26e8ae086f3ae65d9fe7155e120d52b676c0c80281e947e3e24da0000000000000000000000000195d09fcba4ac3f93898e51f6edaa7fbf1f8c0aef845a06963759c548d949203e6878b0c064d67daf09c951b851e3b0df59aa4b50a024ee3e250a00000000000000000000000000000000000000000000000000000000000000001f846a0776a11a4f2c706dbdd123a60354e7800e5e0707f0122fccf126f6cc5690efd17e4e381e4a00000000000000000000000000000000000000000000000000000000000000001f845a078f054f015d4d9bd71e4242c3813cd545468d547a8d9f942c694e17eb74b9218e3e245a00000000000000000000000000000000000000000000000000000000000000001f845a07971e4274935c018c9d131bed748cb1e1d2c7bd6b46bd84155b25b97f9eb5434e3e218a000000000000000000000000007cb86bbe058cf36534f2f723e4b374b97723622f845a079f4aad8a401c1f0439d3b8234961522357b04930ed8a4db24dd356f321be1e1e3e24ba00000000000000000000000000000000000000000000000000000000000000001f846a07a10e3940ba73450375e9d4705dbf4773015540adc5273d161a8a46b5e38520de4e381c4a00000000000000000000000000000000000000000000000000000000000000001f847a07a9d957bace703f411144caef8561539618a43a2aaa2ec3dc20fb1c1318d311de5e4820143a0000000000000000000000000aaae7fd759ac75dbfb5a85b8fc9d93fd3646ea5df847a07b174d0b5f902c4b8cd10249732f3f64910f84a16d713df1d14187329b70162ce5e4820133a00000000000000000000000000000000000000000000000000000000000000001f846a07d707313bf3a7050725b8952e34b0df62bf70b787251a0569a54cbddaa717fdbe4e381c0a00000000000000000000000008e109b88c7765c3e7502781a5e3f034863df9e97f845a07de3d71bd86a6c7f4c3394022c28c979b2cef3a4abde9f61e6ce9b9b13f5d8a3e3e248a00000000000000000000000000000000000000000000000000000000000000001f846a0807a6c3729944cec8388134461a20518b784ef18a72e9e1fbaccdf23b86501a4e4e381c2a00000000000000000000000000000000000000000000000000000000000000001f847a080815ab5e3035a0d4d275219b8e354936a2fc409c91646bc315148b7fb236bdce5e4820143a00000000000000000000000000000000000000000000000000000000000000001f846a0821e2af61cb00de6efbeac2b3d3832ffa089871c1e39c3995d214584103cc9e0e4e381c8a0000000000000000000000000fe93e9ce4f730a1c64846bc782e45904ffaa633af845a0848b9cc617e2947515869b422eb8815a5ad891f80df602b678e1a190f017ba1ce3e242a00000000000000000000000000000000000000000000000000000000000000001f846a086d9cee311b771d6016304cc4232a76243a92929f9600bc10ca7b5a542f1ba72e4e381c4a00000000000000000000000000000000000000000000000000000000000000001f847a087b59b8db4f221eae51302f4190f492996ab1a9c97f5afef6ea315316242a798e5e4820134a00000000000000000000000000000000000000000000000000000000000000001f845a089872db687d669b45f62b97cdfd7839b0f3f56b436dfd69849c67d9b97a8e327e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f847a089c4c403ceb7b392dc63cd1d1ec467bef2c4dcda4b24ead911e1010826d5983ae5e482011ea0000000000000000000000000f6473fa77d6c2b7e4c32936e7bed7b213aa4325ef845a08a0fb0da05d5fec6f3d9ab2d4dca243c78fdc2af895c33aced9022bb050d4ecae3e24ca00000000000000000000000000000000000000000000000000000000000000001f845a08d69ee905693a5e0da42c1950b06403f281aed9aca3896bb135f1bb78ab665d4e3e24da00000000000000000000000000000000000000000000000000000000000000001f847a08e0fc90e14f7b35dad61b5e31d473ee0cf266e09b65f07fb7937e97b598175a8e5e4820116a00000000000000000000000000000000000000000000000000000000000000001f845a092e5d2c1dbcaea1dffcaa0e6d2b6686f822141133e389f29bb35d516c2d1352de3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f845a092f62bc4942b27309081e88c6b601e4848be4e4a0035c179aee26757bcb9755ae3e23ca0000000000000000000000000d85ed0e5b0130e1f9b9f7ae59a1c73387bde4651f845a09a83ab91050a11f0837f4a689ce5e8457b376afdc9bc3ecad73e55dd9d122457e3e243a00000000000000000000000003f8a2b6ff85da8d791190e287965d4fa745a8f92f845a09ac0a89ff900160c78f0e89ec63e48a057d17121f290a91d43eff009cda315f3e3e250a0000000000000000000000000706b46919914e1208a10473f411fc65095e582def845a09b92c75de072ef82c7304c90e3ebd534cca653ddc3d0cdf29c9e420a033ebb24e3e242a00000000000000000000000002e0093c8d59062caf3cc790d7b7adcb3cdd5dcf4f845a0a127a674a51243111d1f1a640a51875c3d76fd6df6288acb6b52f96b5e5ff07ae3e24ca0000000000000000000000000caab29ecae649ae94a24bf7eba52a924ec56935ef845a0a216442c485571b32f196b9eb0e91729e1ae5fd5b9e8b9aa4c9fd2ec543c546ae3e248a00000000000000000000000000000000000000000000000000000000000000001f847a0a3ad83b210ad01992affa9b8dc996bb85a7461ef6344798952b0129b06d268a4e5e482011ea00000000000000000000000000000000000000000000000000000000000000001f847a0a592a60cf6c642b47fed0d3ec2be81c95a96604d7b5274b5e6bc1afc6dac8088e5e482011fa00000000000000000000000000000000000000000000000000000000000000001f845a0a652d818e6a45381a5d95bae38f9e915696bab2fe889a902d7ca6d3861c45ff3e3e247a00000000000000000000000000000000000000000000000000000000000000001f847a0a6ad215d5047e987f16a4ee32610633ece5ff2da3202b2970c3807b5d842eed8e5e482011da000000000000000000000000067a45c5a86ebf8c66958b11b019c6ae44c797f41f846a0a81da1c7edd313c5c065edc65ecb1fa27ad3c8c342e38db248934ef0916db3cbe4e381bea00000000000000000000000008d95149160bae314d8a757c827ed62177c335794f845a0a943987ca627c64beb0180a5aaae1b33705f10e8a726da8096d90d9eae9d0992e3e24aa00000000000000000000000000000000000000000000000000000000000000001f847a0a967185e0d80009ab9a9051e247d544c25a9d3b754495e9f057f100fef9a9ea7e5e482010fa00000000000000000000000000000000000000000000000000000000000000001f845a0a99cac543e9f5e2f6c5fa6d461b9747e456a1f41d8712080f96517b4f2411ee4e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f845a0ab7f0d36420b293b754af5907b5846bd0eb903fbf881546d9c563c2ea6a02b7fe3e244a0000000000000000000000000cab50a2b987fe307553c9ef2f4d4ac574a1ba13ef846a0ac106f08d9f250c929f757323e65cbcc22b0910962fc4f0e44888aa237c5d824e4e381c6a0000000000000000000000000a9c4046eea8f0db337b3394ce83e70529f3eb8eff846a0b02785ccf2a3ba3ef4e5b59173076880eac3bcc577f6e759f39842ea5dd98db2e4e381d1a00000000000000000000000000000000000000000000000000000000000000002f846a0b16c5656476e3e3b0b7ca6376277d0d36c369fa145d558ed10f242913cb0144ae4e381d1a00000000000000000000000000000000000000000000000000000000000000001f847a0b2fc907d21eee1ffd655aeeb7ad16890d7e214907ad667539907ee6e2660e394e5e4820134a00000000000000000000000000000000000000000000000000000000000000001f845a0b5a052ea42b271c75f64df912e20708f230c3a620469c99ffc36651491b0bb67e3e245a0000000000000000000000000d099bc9db95489b6a036623fb2c744f40dac983cf846a0b8839200738c8f45ba65b18cebdaf4646aee9bdb79469be1bba05888574120c5e4e381c2a000000000000000000000000047ff49e729a36560c53125223cd4aa453c53e5c4f847a0baed2eed1c1388b1a88f2b0aa3740acf736e140659d8b9e1aa9268a199e53fcfe5e4820144a0000000000000000000000000e680366019c64871ff35a6996296782e5a25920bf845a0bd54eb358f2945f0608c9aa105f8c3d05c3493fc9deee7078f09489d81e72ebce3e248a0000000000000000000000000a193c160aa1a8971eca9490cafd807250791b7c6f845a0bf87d9bd9339e85ff8f4a162b159565521179504c78504cb5e128c531cf42846e3e203a00000000000000000000000000000000000000000000000000000000000000293f845a0c07319c1fa6bc4790a488b7c067c303dcd977c6646a7a11356d82a097cad2524e3e24aa000000000000000000000000002e376b6d590680d99dc422be8ce718730e15d50f846a0c1b3555dd4051cc61362dc403b0ead6469543e90f9f983446156165ca520a705e4e381aea000000000000000000000000077b2c0784794f28582232e6c73932ae6d1cea4baf845a0c1b3df7a47cfcbdd6dcf2743982dabc92dc5c49fd4c9a81972445921887a07b3e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f847a0c339a88e386e318f3b192e3275308b63c64d7594fe66530245f1d5f87e11a1abe5e482011aa00000000000000000000000000000000000000000000000000000000000000001f845a0c466bcac5bc09509cfcdf22d753b89a57425de93e8ca7283bf25fb431ea5d407e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f847a0c4f88893fc67a77222a3104d67c76843c921e1b0f1fc2c11d762f7dcda7f47f9e5e4820139a00000000000000000000000000000000000000000000000000000000000000001f845a0c81b690cc8dfbd6787d50ba9e14da4938d92a164f417883a505f5fcd9b9ff44fe3e27ea000000000000000000000000054975a68c0609280f67050f3b167d554e89c325cf845a0cb51683c780daf9b68e3599d0ed651c9c9877c2de0c61b8576a6dac86e1de937e3e24fa00000000000000000000000000000000000000000000000000000000000000001f845a0cb98f598a50ee3c4fd5568ea53a7cd5c66c25eee4af794b0bd59c9e0b0cb865fe3e218a00000000000000000000000000000000000000000000000000000000000000001f845a0cc566b326325c6a2ea2fbb0d50374ba1efbc95f8b77551d701407c44f4dd80d3e3e24ea00000000000000000000000000000000000000000000000000000000000000001f847a0d3c2ff8ece0ce5a0b4ec00cc9d277389e3a3486e8163210bb12b32c43a95d6e2e5e482011da00000000000000000000000000000000000000000000000000000000000000001f845a0d3d9c09a7c86eb54c847253ffdcc735ff48132eb9d97b6e97af38e425995dbbee3e24ea0000000000000000000000000e68df48c92b21367043797c24f62c392c5d922eef846a0d40d5560afd88aba969e7916abb940241912f34a735061c0d7494ede6be97f36e4e381f8a00000000000000000000000004100f0730b8771856e4200e12434c4a19f1057ddf846a0d4936ed3cebbb3694b184ac208cad12289038e99d69e356156c87fdaac6e7978e4e381d1a000000000000000000000000088d0e9fd5058114be1df181271fc979beb66dcecf845a0d51721ea8ad9101fbc35f3b74dfe91988605f5008323fde5a0545c7671efae39e3e24ca00000000000000000000000000000000000000000000000000000000000000001f845a0d73267d100a4dbfb504103f49869d310fc07b9c95e958afbb93707cd55421b5fe3e259a00000000000000000000000000000000000000000000000000000000000000001f845a0d87721e0b18aac57e5e679f2e8179177adece5a67e5ee09f2b70d5198f54e3bee3e241a00000000000000000000000000000000000000000000000000000000000000001f845a0d8ad4158b0c8fe304213511da307e5f9f0193f64d41ef9616c862a80c62c9ee5e3e241a0000000000000000000000000a166d72fa029fe68b2e9d8364f9ca88a7814dbb3f846a0d9762b3ebb67e80f5885b83c7e5bb849c785e81fe30fc10cd23494aaa8cd03c0e4e381c0a00000000000000000000000000000000000000000000000000000000000000001f845a0d9e0efe215ec3b3f0aad0dd4b80d6d8678d0ae21f2abdbd0c95d5ea602297ba3e3e203a0000000000000000000000000e4166f06730fe19e6487a56ba79b2371b4c52515f845a0da45eef48194f42560b44b2ed3ed260424c479b4d59d2ee3c985bd92251088d0e3e246a0000000000000000000000000f093f201d85872748a3a1f8f09808e713cd9c0f9f847a0db89b42158ed8be4e19e9b513a17603adc01d82285eb6b8a179745163c2e832be5e4820144a00000000000000000000000000000000000000000000000000000000000000001f846a0dc791cefeaad24bed83f45fe76340c75b62f04da19999ec2187bee4620c6b596e4e381e4a00000000000000000000000005ebab6791bb90ac6acc1afa394e440366b933310f845a0dce3a67201b0e7b3fbe984f3f4ad58dc2e775b5746393e423899faacb8f2abefe3e244a00000000000000000000000000000000000000000000000000000000000000001f847a0dd0423bcf9c9674ac81c61493f169b3c401f5e85ec9b65e9ee7552b91866fd91e5e4820144a00000000000000000000000000000000000000000000000000000000000000001f845a0e010dade8cfbe9b415531440b7e6661a7b62c3a89fa5e640e0d1cb0aab9fd8b6e3e245a00000000000000000000000000000000000000000000000000000000000000001f847a0eca5db0165b2f81abff22c0b25b78707b5cf982eae670799ffc41805082e5191e5e482010fa00000000000000000000000004b95963192df7c7e5fe2c017997a7c281a3be2cff847a0f0e201cd2eb08e6a485841676fa1a96223a27de19e0f201dfae92522cce3b79be5e4820139a0000000000000000000000000ae134de48af0f087472b89b042952e09407e40a1f845a0f158d968c784c3142fe9198550c34e11bfa0e278502a62dce509bdd6bb689776e3e249a00000000000000000000000000000000000000000000000000000000000000001f846a0f6bb257887ba95d45ffaec94c1fffc735130b4e252fd9ee3de784529c5a04656e4e381f8a00000000000000000000000000000000000000000000000000000000000000001f846a0f8ee8e02a727f24e2944383e957ac2e7e7cd1dda8d4ec16d54199001923dbe79e4e381c6a00000000000000000000000000000000000000000000000000000000000000001f846a0fbadf8dd63a027e0be7c0dd0a658dace314b3d62785898f970f1de0a14000735e4e381e4a00000000000000000000000000000000000000000000000000000000000000001f845a0ff5a974c8ef6aee2df55c5c4cbed3366de67d1233a3bb9cc3012d828263bfb4ae3e247a00000000000000000000000000000000000000000000000000000000000000001f845a0ffa78031d05b20df28b5ee39c910bd65e77284345dd71443259f9259e3d31755e3e250a00000000000000000000000000000000000000000000000000000000000000001f9016ba00000000000000000000000000000000000000000000000000000000000000007a00262dc66aa8f41171fd409327f2d6b94350b81d726e9ef3a2527feef9e0f7aeca02a4d8d92661a9803e64ff15fba03fd8c139581844906f7dbbac7e01a798e92f0a04b73de2ec11cd2486ec445ba87fa2c8c267bb14782c0c9096fb840a54e37cbe2a058578b504a53f35a041324e4c0d63a996ac8c16109ce6523a8d774c45067cc24a076ca87635bd64b3606d36ab664f6ad0c59124ffdc0f3f79c92d7de94638dcc94a08d942c9de457954de17f931e3ffcc6eff41b684c26a099ff06e23d121228456aa0928408311edd485229c0b057a43d2b87950b1e0fc73fbe481e8e939ee2b2f9afa0c13b37751d34b04c93efdb606b63bdf3bfa7b486cf111771c55b16bbf00ade87a0e7ef37b076cd1bc8f1dda41378362ac154b13d47592bf2241d7955a9356a0bada0fe7a1e6b7aa2e418614e65c8a093c329f5ee9d3169986801a72febb24894dc42c0c0c0f29426fbe4173188c73439d2eccab830fbb3f7547b74c0c0d4d381d09000000000000000000000000000000000c4c381d004c0f09427b4342d9f9504c7667c34401931b9873304f632c0c0d3d2689000000000000000000031b6472cc3b97fc3c26802c0f9015494284e590de09e82b6706e4dff386fc678a01250e7f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20ea0688a1197000000001033ee67bc14b0f9015900000000000166a4cfd0047359b5f845a00000000000000000000000000000000000000000000000000000000000000009e3e20ea000000000000000000000000000c0f9661a1c26b76efc99ea9c031d26592abb1bf845a0000000000000000000000000000000000000000000000000000000000000000ae3e20ea000000000000000000000000000000000c858eec0b006bf2339f46f84dea2ea7df863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f8bd9428c6c06298d514db089934071355e5743bf21d60c0c0f878d381b590000000000000153469f2ae3dd9c461dbd381b690000000000000153469f2126cd23c95f8d381b790000000000000153469f17b691b943649d381b890000000000000153469e82ee1dda240f1d381b990000000000000153469e7da95f1c35215d381ba90000000000000153469e76b4304e3c29deac681b583c66ff2c681b683c66ff3c681b783c66ff4c681b883c66ff5c681b983c66ff6c681ba83c66ff7c0f87e942905d7e4d048d29954f81b02171dd313f457a4a4c0f863a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000006c0c0c0ee94290e92c9177c7151cac5c9b98a04e2dceacf4819c0c0d4d381aa9000000000000000000163b29c0ae98000c0c0da942ad5004c60e16e54d5007c80ce329adde5b51ef5c0c0c0c0c0f2942b34ea1459b796203a2b785a86772da5cf09c0bfc0c0d3d26290000000000000000003b912efa477b128c5c46282070cc0f90356942c158bc456e027b2affccadf1bdbd9f5fc4c5c8cf901cdf8afa00000000000000000000000000000000000000000000000000000000000000005f88ce25aa00000000000000000000000000000000000000000000000000000000000000fc6e25ba00000000000000000000000000000000000000000000000000000000000000fc7e25ca00000000000000000000000000000000000000000000000000000000000000fc8e25da00000000000000000000000000000000000000000000000000000000000000fc9f845a00c8a0a486ab5533ad3fb97b3edc5d3d323813834c7a71049a0465e4b4d5ed62ae3e25aa00000000000000000000000000000000000000000000000000000000000000001f845a061161c9978bb25a1db74988204dfbb00785f5a55cfd5ded98b1c44eb166d77cee3e25ca00000000000000000000000000000000000000000000000000000000000000001f845a08db09278c49865221f6f974add0c494689f5b5627205e38452d3b93a250a7323e3e25ba00000000000000000000000000000000000000000000000000000000000000001f845a0f85c551a484165b827e47bc8ee7990004c36ce3c8c1eab4e839e17f9597bdd4ae3e25da00000000000000000000000000000000000000000000000000000000000000001f9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea0000000000000000000000000000000000000000000000000000000000000000fa00e3573256455e043764e1015e46264b82dbc5a70bbda3551e3d797fad2854f8ba03675391838767b4f684656f1b7730f0b957e91f2e46c383064036757c752d3a1a064ed8176068ca759d2bede4084e0e4d76c7bce2e149d6706bb0c102c6cd3baeec0c0c0f87e942c4c28ddbdac9c5e7055b4c863b72ea0149d8afec0f863a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca088d075c869ce192f20da9bfc0d2db81b73b4aa4af2ce17e52384cb021d06bd06a0b78fd7e1706dee4e251f6f3c52649313d4e032077a25f6c26ef8fbf58f357651c0c0c0ee942cb0f4968e89d66d1aa806155d601b34dcc1e3f4c0c0d4d381cf900000000000000000000f7c3b95468000c0c0f4942d23ef144a3665c4d6b668cb3efd3c9ce3db129bc0c0d4d381ee90000000000000000000002f9c551c5c56c6c581ee8204d1c0f0942e0093c8d59062caf3cc790d7b7adcb3cdd5dcf4c0c0d3d24290000000000000000000074516d2822081c3c24205c0f9025d942e3fb0298a8dde91860ee8d7b1b0a11d7a1101c0f8d5f845a018e1511117a28e3cc8a208df7264328791cc690bdf8a207492fa6b0ccac7e423e3e20fa000000000000000000000000000000000000000000000000005504d85965decfdf845a02cf1005e2b00dd50583ecb1ad409063b364c1fbdb904b4132058210a989ac7dae3e20fa00000000000000000000000000000000000000000000000000000000000000000f845a035b37a6bab11b7263dcb2b876306c908042aefa17c85164ca45c37c48aca2ca7e3e20fa0ffffffffffffffffffffffffffffffffffffffffffffffffffd12eac44aa5f76f9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000011a00000000000000000000000000000000000000000000000000000000000000014a03dbf2ca99ffda4398b43eef828f27c7f9231dc34c5a1d61a224b1e8b47ea05fea06a4f0d0d80ea1dcb80998dc73cf75d4f9f7165199cf70b8d8c7e34ff34c33708a0c626547b4f99cf47c579146a6a1c4ddbd9430da66725a8d1bff899ef495683e6c0c0c0f2942eb26622a353b29553e16273d4ec27c5f2f2a7a4c0c0d4d3819d900000000000000000000131c2ae2a225ac4c3819d02c0f9021b942ec24fc75cf4dcc8475a48b3f6a880dbc6901ecff8d5f845a036ebda387537566728900b76db13103a5417b9b66c546474c5cd1783fdaad81ce3e20ca0ffffffffffffffffffffffffffffffffffffffffffffffffffe006328b0057b8f845a082dad63db9b278d69b9b7ef8bd34ca0ff7fd0b069419bfc8e46ffdd818b27626e3e20ca00000000000000000000000000000000000000000000000000000000000000000f845a0ec4e86c2e8ab3a5a06850e4ace3bd24b781fc1a4c942390cb4c357f1bf17056ae3e20ca0000000000000000000000000000000000000000000000000005a3cd5ba74498df90129a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000012a09926c02dd9b42803c52558baef716ee12c3060b8ab7defa19dccea4896e93582c0c0c0da942f18f339620a63e43f0839eeb18d7de1e1be4dfbc0c0c0c0c0f85d942f39d218133afab8f2b819b1066c7e434ad94e9ec0f842a00d2c1bcee56447b4f46248272f34207a580a5c40f666a31f4e2fbb470ea53ab8a0740f710666bd7a12af42df98311e541e47f7fd33d382d11602457a6d540cbd63c0c0c0f85d942fe934eb46720a56e64e28ab30f1caa926288bb8c0f842a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f294308f544d88aecd34016781b9f3cba30a2e524eefc0c0d4d3818d90000000000000000029a1fefdb9eb993fc4c3818d0ec0f29430eb78f620b23f81865b346e3d4432c8e39a78a9c0c0d4d3819390000000000000000001e5f6ff02b1618bc4c381934dc0f2943131c77f79513efab8d703176f831dc3870d5557c0c0d3d23090000000000000000008c1661a09d46993c5c430820c91c0ef9431628c318662be176a1559ec8b7c4ad7744418cbc0c0d5d4820129900000000000000000004380663abb8000c0c0f901339431a43eb48625239d0bd2d2213a6202b3eecbecf2f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e26fa0000100000100010000ff7d670000000000000000301db28dfa2f448217eb123ef845a00000000000000000000000000000000000000000000000000000000000000001e3e26fa00000000000000000000000000000000012c9b11e774f7dcfaa3f939beeb22e51f845a00000000000000000000000000000000000000000000000000000000000000008e3e26fa0010000000000019d51ee34bfd3a2e9511e7db7627cffffbe41899a28688a1197f842a00000000000000000000000000000000000000000000000000000000000000004a063187d71e139eee983a88d0737447c7451979b3dbb75903c76b5fe430d36588ec0c0c0f09431da337b2a1409e8fde14e1f49d487a8b72f3018c0c0d3d21b9000000000000000001c07f53f61c71bdfc3c21b3dc0da943225737a9bbb6473cb4a45b7244aca2befdb276ac0c0c0c0c0f8ef943328f7f4a1d1c57c35df56bbf0c9dcafca309c49f86bf869a00000000000000000000000000000000000000000000000000000000000000001f846e20ba00000000000000000000000000000000000000000000000001ad362f0665fc7c9e20fa00000000000000000000000000000000000000000000000001ad567b3f6feb8cbf842a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103e6d20b9000000000000000001ad362f0665fc7cbd20f9000000000000000001ad567b3f6feb8cbc0c0f83b94337685fdab40d39bd02028545a4ffa7d287cc3e2c0e1a0d138504697de92b7ed8a8aae629d27d26630b4fde8dd22e85a384a6e6c5b456ec0c0c0f84e9433b99ccbc84e8a87e10ec86386a7eb5ad434fb7ec0e1a00000000000000000000000000000000000000000000000000000000000000000d3d21d900000000000000005f7ba1621c5ef8797c0c0ee94354d3b3a8393c558ddfa03fa8c2a229d9714817fc0c0d4d381ac9000000000000000000002d79883d20000c0c0f394354e9fa5c6ee7e6092158a8c1b203ccac932d66dc0c0d3d2609000000000000000028c285f05753ac0e7c6c5608303d002c0f9010d9435d1b3f3d7966a1dfe207aa4514c12a259a0492bf88ef845a02f0515d3e6804b0792136a69384e701be39b327661d6725ed995364120a01e72e3e208a0000000000000000000309408842186d963c3c8660f1801968dd5e88898000000f845a07adce341587c7ad28f80a9ef54da0d2f0bfd917b11856be2d246a8c66685b1a0e3e208a000000000000000000027072c8bc198476fed123181286a0310d67b8b00000000f863a03ea716f0f3cd66e12a20758b3a58856947ad2da16282984f676db0ef6a0e8a05a0642c1ef9ff934c392343f46c239eb029ededdd3ab04b8cb7aa8cc5db70738602a0c14b81a281327ab8314e03b228c5bfd656d25a645058ed894ce584e2dd01ac56c0c0c0ee9436475477b99a3b9bd11846aa3e3830fde60278e9c0c0d4d381ea9000000000000000007c9eb34f61e9a616c0c0f49436ab40ec7001e10f440ddc143be2e0cefe22ceaac0c0d5d4820113900000000000000000000028d94b22a5d1c5c482011319c0f09437f20c3c40ab16332851958c1d2e311ef2b4e4b2c0c0d3d210900000000000000000000c90b14692ba46c3c21021c0f09438144eac0ed090a9f8f36c110e7d20544cdc6191c0c0d3d2799000000000000000000000000000000000c3c27956c0ee94382ffce2287252f930e1c8dc9328dac5bf282ba1c0c0d4d381fc900000000000000007b0b07bfb47e6c411c0c0f49438d46ed543b8e646cea1e640097a1026a6b21f5fc0c0d5d482010e900000000000000000000932ab15a3a986c5c482010e0bc0f294391e7c679d29bd940d63be94ad22a25d25b5a604c0c0d3d27d90000000000000000000ca8098f1373ca2c5c47d821fc9c0f494394311a6aaa0d8e3411d8b62de4578d41322d1bdc0c0d4d381db90000000000000000005a2674b6dab5f6cc6c581db8224c5c0ed943943f80e410ce8408e4fc59c2e3f8fbcd963a469c0c0d3d25490000000000000000000551b34d6dfb828c0c0f9015a94397ff1542f962076d0bfe58ea045ffa2d347aca0f8dbf847a00000000000000000000000000000000000000000000000000000000000000008e5e4820142a0688a119700000000001659a19cff1f0d8c60000000000000000001699c721b2cf847a00000000000000000000000000000000000000000000000000000000000000009e5e4820142a000000000000000000000013f3b21c5ba2f94bdcf133095164bf606524e24a524f847a0000000000000000000000000000000000000000000000000000000000000000ae5e4820142a00000000000000000000000000000000000005a8ac9b4dcbba04f49707f373f2cf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f902799439953e6429210c39d11688fa40219c811287caadf88ef845a00000000000000000000000000000000000000000000000000000000000000002e3e20ea00000000000000000000000000000000000000000000000089f8c3eaa3770967af845a00f12b799b59d030df91357dbc659788be3afe85ed8ab31fb27b990a45e64d037e3e20ea000000000000000000000000000000000000000000000000166a4cfd0047359b5f901cea00000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da00175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9a00175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbaa00175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbba05badfc95b22ecec658d9412b095439c9f361ac60e252f54add70f3bc8a4b4177a05badfc95b22ecec658d9412b095439c9f361ac60e252f54add70f3bc8a4b4178a0c87f3dae70d3ac23e82424b4132d779583ae145fc334a52160c2d1a246c2bc2ba0c87f3dae70d3ac23e82424b4132d779583ae145fc334a52160c2d1a246c2bc2ca0d83ad78b506c893b2f5085be44098e29b515bc23bda8d0bd00bb6197ae79c921a0fcac38389b0ae9c7b76ee4a3db54fd3e6e6e102c6c49fbef5ee1e04c33e64576a0fcac38389b0ae9c7b76ee4a3db54fd3e6e6e102c6c49fbef5ee1e04c33e64577c0c0c0f901329439d5313c3750140e5042887413ba8aa6145a9bd2f892f847a06a8d1776a0e0bdd5bf66be72fdccda03186aedc4dd2f4efc89504913f5a04f83e5e4820141a000000000000000000000000000000000000000000000023710b84eb2f2b9acfcf847a08780aab2c059dd07998a4b329d71b1304a73cc15a6613407133364f4a1a92adae5e4820141a0000000000000000000000000000000000000000000000000ad6b6b832cf3544df884a03169387b6397812ad2dc3dbab8209e79125d6d660efb8f584892a9a17e69b2faa0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca09016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300a0cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300c0c0c0f69439f8670420c75b2631021e823ad6378332396fe3c0c0d5d482011d90000000000000000017d793d3d1b5d8e3c7c682011d820352c0f8e6943a10dc1a145da500d5fba38b9ec49c8ff11a981fc0f8a5a00000000000000000000000000000000000000000000000000000000000000003a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca05ca141d3f9e350a91781a19938bb2685e08c79fd2d64ff66623659dc746ae186a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0bb07cda484c6b77b83179e90ab7f7417d246ee46d6cffb991494116df8bd6769e6d20a900000000000000000ac8df8e54835a11cd20c900000000000000000ac98fc60fdefbab7c0c0f845943aab9b3145df48a22ae337d50d1d66a3b2a3bda0c0c0e6d2039000000000000000000835423ef624b593d26e900000000000000000082f6c3bcb88ed63c5c46e821f9bc0da943ac05161b76a35c1c28dc99aa01bed7b24cea3bfc0c0c0c0c0ee943ad13a5b6557446d9788ac8943775dc9380481fec0c0d4d381969000000000000000000000e7ebd06af000c0c0da943c0f895007ca717aa01c8693e59df1e8c3777febc0c0c0c0c0f87e943c11f6265ddec22f4d049dde480615735f451646c0f863a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a01a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050fc0c0c0f0943d2f4de780472f5ada58707b50da12a1cac14596c0c0d3d26a900000000000000000003d729a2a2f2ad0c3c26a13c0f4943d30a58d75e84bdeb8c54b4839cd6eb048ce5852c0c0d4d381989000000000000000007246909db88fb2cfc6c581988203c0c0ee943d5d35899ebf2967e9c7c58afe4371681863f68dc0c0d4d381e790000000000000000000061aca1973cb58c0c0f85d943e7d1eab13ad0104d2750b8863b489d65364e32dc0f842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005c0c0c0ed943eb45601c4b3f7fef6a4e8c99410c967f814af7bc0c0d3d20d90000000000000000003aa39cb0ef4ccd2c0c0f2943ed1d0cddbd28be822c8d44a58047d87cb2a5b08c0c0d3d26f90000000000000000007741365b304dd9cc5c46f820692c0f83b943f73f03aa83b2a48ed27e964ed0fdb590332095bc0e1a00000000000000000000000000000000000000000000000000000000000000002c0c0c0f0943f8a2b6ff85da8d791190e287965d4fa745a8f92c0c0d3d2439000000000000000000078235a95125c6cc3c24301c0ee943fe5a843da0c582ad36dc066961d1241ca496e98c0c0d4d381f59000000000000000005edffbd71d440c22c0c0f29440196a1dc6e27bf650c090f8ca243332758f028bc0c0d3d25b90000000000000000001bc564cc0a52481c5c45b8203c3c0ed9440995fd172c745559432b1ed5e753dcaaee9a94fc0c0d3d256900000000000000000960830eddc42e58ac0c0f83b9440aa958dd87fc8305b97f2ba922cddca374bcd7fc0e1a00000000000000000000000000000000000000000000000000000000000000065c0c0c0f8c09440c57923924b5c5c5455c48d93317139addac8fbc0f8a5a019706acfa9d2e31e428368727f9392c708911c02f840270637707ddbb43164f4a024228eb7b5b16f25220decd05b5f6b9e16f15e54f3b304109c433b9dece0be4ca02eca26cd308bad6efd2b97274931094db504b833b663fd64ce183284b1f3d0e8a0a1c9e0290ca6c4de8f39b7226c2675ebece747467385dfcae54a36af04e73b42a0fa98da98dd68729325da45b3f03194f6462ca6f95d2ea2f716d76f0a7bdf07abc0c0c0f902d99440d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2ff90238f845a00fb186ed409fb55c160c88b3a2bbb4dfc38770e045ecd36537badf3dd35609bae3e208a000000000000000000000000000000000000000000003122ece76599b4cd8a2aff845a03388518c3d41ceee0101c43655cee68cb09523570c301f91829bf14fb3d19568e3e208a00000000000000000000000000000000000000000000000000000000000000001f845a0716b2abbdf59a791bc7405e16e39229cd2c5d3cb861b829e0ea05cf161cbb9c1e3e208a00000000000000000000000000000000000000000000b2724849870a7759117aef845a08c241777840efd6e6869fe5d7e7705ca1dbe8cfd17ea110a57f6e6ebe85c4a9ee3e208a0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845a08e485ef046749f65faa7aa167bdfe2d6d40cf685615648f709078e0cb9978a8ae3e208a0000000000000000000000000000000000000000000000545ca060a6df79f7e6af845a09d98752c354deebddd53535455198eacf8cfb934237d3523207f70386be5e3dce3e208a0000000000000000000000000000000000000000000000008ab4a102244f6c9f7f845a0bd526339cd386d579e5f59ddf30d855fc94193920d5dc39efb74be6613369732e3e208a00000000000000000000000000000000000000000ffc8947aac8134f29deff424f845a0f97069568437baf44d453bc59f46a2a35b3d69e489355c43c42aa8758f493461e3e208a00000000000000000000000000000000000000000000000000000000000000000f884a05414e47ea6d7a1b524a5e945a70f59c722a19981dfd15bb0b46b4d0fe64a6d3da05665cabdc5830e97eccee5b2c7ca740e4f87cded540788a9587cfb71db89a9ffa09d29ddfc8945dd05453aec23c6d0297290596dacbf751bcb0cc9ba8f8b8c0658a0d746e82bcbc0d36cbe5d00c038700ff4c469dde63202d9fc0d7a116218422b91c0c0c0f2944100f0730b8771856e4200e12434c4a19f1057ddc0c0d4d381f89000000000000000000000d7a5b6b5fe13c4c381f801c0ee944185e3ba2861c31189e9dde816d55acf96a48c44c0c0d4d381e290000000000000000000a2c7dbf09fcd18c0c0f902609442069e779838929495ed0152ffc27145ce5c7f98f8d8f846a07d97b657a253d2ad17db2ccb2bd132924a108741808865a71ac7f53303fad65be4e38184a0000000000000000000000000000000000000000000000dc921fa94b282387beaf846a0ac888ba83bc9dea591f6bdd5d5ca99b3b39e2f7003164f691afd3003f5d63749e4e38184a00000000000000000000000000000000000000000000000000000000000000000f846a0de36076a70d7e04b58a1504ce475124a0ac66ec1cf217b1c7ade70ebd959e1eae4e38184a0fffffffffffffffffffffffffffffffffffffffffffffff947fecbafc5e11bfbf9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ca00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000013a00d80ddd128d9961c8eb52e7a2730e4bf9f454e0830fd51e14407f7de9858eb1ba01f1ec3178783207d382bde9b7bedcd0061b68f650f540d6980d95593a44066f6a072e52795d031fea1a082e68f783b729768b75aca0b02da91a411401ba66f75fcc0c0c0f2944255d5c59c9a23ca51fa31f9ad1916d1f1da6301c0c0d4d381819000000000000000000004e09337932a4fc4c3818102c0f83b9442bc86f2f08419280a99d8fbea4672e7c30a86ecc0e1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0f87e944305fb66699c3b2702d4d05cf36551390a4c69c6c0f863a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca06def1416485b70233832df230fd802914ad65c17b325a7ff495c9ffb27cda201a07749865cc250abcd902df3f9b06d804adfdaa67cc5a070ee1f6409f32a8ff15ec0c0c0f594435ac6bc32b01a5b7e749f90a6a580f70f3af04ec0c0d5d48201299000000000000000000a6b7c80e23f9818c6c582012981e2c0f87e944515b64147b8eff871929c90795ae9ecb542f162c0f863a02e87e72e5b8d4993f1c5d68b37d42ab10d71fec7cbdbe6527e44ebe90207e3f7a06378b2e17ea75746272f244cbc4e08201823b700669acd0856252352668b0742a092c714bf53756b5954d914075b684440a5b6b3a6b0cbd9be79b08c77434a2551c0c0c0f87e9445312ea0eff7e09c83cbe249fa1d7598c4c8cd4ec0f863a00000000000000000000000000000000000000000000000000000000000000000a0ea4369cdd784effddb5849de3d072f82a40d15d3d81f0de55b8b181d509bc8a8a0ff68c7e56b106d8ffb900910533f72176d9e07f6ada08105f291dff3669a62b4c0c0c0f90154944532090213cc1b87b7a5293cae58680b867dc69df8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20fa0688a11970000000000001d53f9eb7714a54100000000000005504d85965decfdf845a00000000000000000000000000000000000000000000000000000000000000009e3e20fa000000000000000000000000000000836c904a1fff29bdf48a46814aee83238ccf845a0000000000000000000000000000000000000000000000000000000000000000ae3e20fa00000000000000000000000000000003d34f2c4e62f6a10405d8e2ffed14929ecf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f85d944579a27af00a62c0eb156349f31b345c08386419c0f842a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca09d9574a64928c994d335836e3e6a2a2847be75de0c5847bba8d7a923b835a249c0c0c0ee9445831b407b5680d3439a967d8adf1dc43fc14c3fc0c0d4d3819c90000000000000000002fa1dc1ffbd4dc8c0c0f8ec944585fe77225b41b697c938b018e2ac67ac5a20c0f88ef845a00000000000000000000000000000000000000000000000000000000000000000e3e20ea0000100012c012c00bd0409e100000000000885b3d462b7ee2982da20761ce48bf845a00000000000000000000000000000000000000000000000000000000000000002e3e20ea000000000000000000000000000007efcf9b1c7019513427cad51e110dd9332c1f842a00000000000000000000000000000000000000000000000000000000000000004a062552337360b040ac00905130c40c0e814a32ae1bf4d7c7b02730b479e5eef98c0c0c0ee9445f61d9a081ab8ca2005d6434a54ed4842a80849c0c0d4d381e5900000000000000000005615d84dbaa810c0c0f9027e944628a0a564debfc8798eb55db5c91f2200486c24f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e201a0000100003300330009fef1b50000000000000000080cbaa9798ad72034f3127ef845a00000000000000000000000000000000000000000000000000000000000000002e3e201a0000000000000000000000000000000000944cb33db277d22833f94599c78aa1ef845a00000000000000000000000000000000000000000000000000000000000000011e3e201a0010000000000000000004123d28c5a38c2e3283369fff78f100e69a8688a1197f9018ca00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000010a04a6f485c1c32cc07b41a962ff745e1d2a15f32520f92181d93f06ac057458486a0b35704a07132c2ea4ca6cef1e774d02e5bf51e9136708281402ce2deff80628ba0b35704a07132c2ea4ca6cef1e774d02e5bf51e9136708281402ce2deff80628ca0b35704a07132c2ea4ca6cef1e774d02e5bf51e9136708281402ce2deff80628da0b35704a07132c2ea4ca6cef1e774d02e5bf51e9136708281402ce2deff80628ea0b3fb4ee8142aab034b0eec5d60e08ed1e69d38201f0242c94e8e18c9f0ba0980a0b3fb4ee8142aab034b0eec5d60e08ed1e69d38201f0242c94e8e18c9f0ba0981a0b3fb4ee8142aab034b0eec5d60e08ed1e69d38201f0242c94e8e18c9f0ba0982a0b3fb4ee8142aab034b0eec5d60e08ed1e69d38201f0242c94e8e18c9f0ba0983c0c0c0f49447ff49e729a36560c53125223cd4aa453c53e5c4c0c0d4d381c290000000000000000000698c38b6f2fa45c6c581c28201f1c0f919c5944838b106fce9647bdf1e7877bf73ce8b0bad5f97c0c0f9192cd2019000000000000000010f482c774f0bb8f6d2029000000000000000010f56d9731df15bb0d2039000000000000000010f5ecf2ef7d15bb0d2049000000000000000010f61b4dc2abf9fb0d2059000000000000000010f61d81c90fa4ab0d2069000000000000000010f6307287f5049f4d2079000000000000000010f64882eb323d7f4d2089000000000000000010f65107f57664744d2099000000000000000010f6a6102cea1e3e9d20a9000000000000000010f6dd1063ed8ede9d20b9000000000000000010f70fe71d2f8cfe9d20c9000000000000000010f73c95cae0b47e9d20d9000000000000000010f757bfcdb454fe9d20e9000000000000000010f7722902f103333d20f9000000000000000010f784ca8c16f5333d2109000000000000000010f796f5eba90b333d2119000000000000000010f7a59d06a207733d2129000000000000000010f7a71eea9bd03a3d2139000000000000000010f7a85081b4753a3d2149000000000000000010f7b3a413e07b707d2159000000000000000010f7b4d5aaf934f27d2169000000000000000010f7c25bf010af6e5d2179000000000000000010f7ce3658c62a4e5d2189000000000000000010f7d99592e68e8e5d2199000000000000000010f7e46f0e27e280fd21a9000000000000000010f822116cad7193fd21b9000000000000000010f8223497350d6bfd21c9000000000000000010f82a2102df7f73fd21d9000000000000000010f831af0451264e3d21e9000000000000000010f838dea410e18e3d21f9000000000000000010f83e1c96e2fdce3d2209000000000000000010f845309808dece3d2219000000000000000010f84c1dd76ee08ddd2229000000000000000010f852bd4cbb445fcd2239000000000000000010f8594f96a466592d2249000000000000000010f85fe1c679cff21d2259000000000000000010f8666a14d640f21d2269000000000000000010f86cb869a437f21d2279000000000000000010f8729113bfa9321d2289000000000000000010f878690b0abc721d2299000000000000000010f87e41b5262db21d22a9000000000000000010f8838e530ffff21d22b9000000000000000010f888da9791a3321d22c9000000000000000010f88e27357b75721d22d9000000000000000010f893732094e9b21d22e9000000000000000010f898bfbe7ebbf21d22f9000000000000000010f89e982cff63321d2309000000000000000010f8c5d961121d321d2319000000000000000010f8cb17ad4c68721d2329000000000000000010f8d01f2b567dd21d2339000000000000000010f8d5172e69d6521d2349000000000000000010f8d9e712dd17b21d2359000000000000000010f8deab24a6e8381d2369000000000000000010f8e7710e818d781d2379000000000000000010f8eb4e87585ef81d2389000000000000000010f8eeaef10a1d381d2399000000000000000010f8f1ed81b565b81d23a9000000000000000010f8f4c7be3cb8629d23b9000000000000000010f8f7a1fac40b0d1d23c9000000000000000010f8fa79c94c241d1d23d9000000000000000010f8fd5197d43d2d1d23e9000000000000000010f90060c8716aed1d23f9000000000000000010f9036ff90e98ad1d2409000000000000000010f90647c796b1bd1d2419000000000000000010f9091f961ecacd1d2429000000000000000010f90bf764a6e3dd1d2439000000000000000010f90ecf332efced1d2449000000000000000010f911a701b715fd1d2459000000000000000010f9147ed03f2f0d1d2469000000000000000010f917569ec7481d1d2479000000000000000010f91a2e6d4f612d1d2489000000000000000010f91d063bd77a3d1d2499000000000000000010f91fde0a5f934d1d24a9000000000000000010f922b5d8e7ac5d1d24b9000000000000000010f9258da76fc56d1d24c9000000000000000010f9286575f7de7d1d24d9000000000000000010f92b3d447ff78d1d24e9000000000000000010f92e151308109d1d24f9000000000000000010f930ece19029ad1d2509000000000000000010f933c4b01842bd1d2519000000000000000010f93d458193d37d1d2529000000000000000010f93fc1697a1e3c2d2539000000000000000010f94224e5e6915c2d2549000000000000000010f944881417db5c2d2559000000000000000010f946eb4249255c2d2569000000000000000010f9494e707a6f5c2d2579000000000000000010f94bb19eabb95c2d2589000000000000000010f94e14ccdd035c2d2599000000000000000010f9503ae21512247d25a9000000000000000010f956b96df582b82d25b9000000000000000010f95d175bb15bb9bd25c9000000000000000010f9637537228a7b0d25d9000000000000000010f969d31293b93c5d25e9000000000000000010f9743db606347c5d25f9000000000000000010f976314383d01c5d2609000000000000000010f977fba628c79c5d2619000000000000000010f979c09018341c5d2629000000000000000010f97b846613569f5d2639000000000000000010f97d2d4af6ddaf5d2649000000000000000010f97ec93f22b684dd2659000000000000000010f9804a3d4743d4dd2669000000000000000010f9a2b415d3cfd4dd2679000000000000000010f98202bb4e4ad4dd2689000000000000000010f98352e1830054dd2699000000000000000010f984a256c42e24dd26a9000000000000000010f985e9280c12c99d26b9000000000000000010f9871b15f52d81cd26c9000000000000000010f9884cad0dd281cd26d9000000000000000010f98979230cfca86d26e9000000000000000010f98a98b9f768a86d26f9000000000000000010f98e703d4c98386d2709000000000000000010f98f7e53295a6a6d2719000000000000000010f9908827dbc0a6ed2729000000000000000010f9a48a9abf3826ed2739000000000000000010f9a57c3239ec17ed2749000000000000000010f9a669ce71f04c2d2759000000000000000010f9a756f939f28c2d2769000000000000000010f9a83f9df57ba3ad2779000000000000000010f9a91f02fddd1a2d2789000000000000000010f9a9ef1213232a2d2799000000000000000010f9aabc59d0e190ad27a9000000000000000010f9ab89a18e9ff72d27b9000000000000000010f9ac56e94c5e5dad27c9000000000000000010f9ad24310a1cc42d27d9000000000000000010f9adf178c7d60a2d27e9000000000000000010f9b0c9474fef1a2d27f9000000000000000010f9b3a115d8082a2d381809000000000000000010f9b46c363a88f62d381819000000000000000010f9b9b30778fd4d2d381829000000000000000010f9ba7a17402251ad381839000000000000000010f9bb38deff1f1aad381849000000000000000010f9c9bc56bdf53aad381859000000000000000010f9d80380e26d5aad381869000000000000000010f9d8b1f404c72d2d381879000000000000000010f9d960672720ffad381889000000000000000010f9dbbd605e2ffcad381899000000000000000010f9dc616c5db402ed3818a9000000000000000010f9d4610d920fdc5d3818c9000000000000000010f9d6c43bc359dc5d3818d9000000000000000010f9d92769f4a3dc5d3818e9000000000000000010f9db8a9825eddc5d3818f9000000000000000010f9dcfb745985955d381909000000000000000010f9dcfb918cb4f89d381919000000000000000010f9dcfb9a09ef299d381929000000000000000010f9df20a18437c99d381939000000000000000010f9dfb96d108a499d381949000000000000000010f9e052389cdcc99d381959000000000000000010f9e0eb04292f499d381969000000000000000010f9e183cfb581c99d381979000000000000000010f9e21c9b41d4499d381989000000000000000010f9e2b566ce26c99d381999000000000000000010f9e34e325a79499d3819a9000000000000000010f9e3e6fde6cbc99d3819b9000000000000000010f9e47fc9731e499d3819c9000000000000000010f9e51894ff70c99d3819d9000000000000000010f9e5b1608bc3499d3819e9000000000000000010f9e64a2c1815c99d3819f9000000000000000010f9e6e2f7a468499d381a09000000000000000010f9e77bc330bac99d381a19000000000000000010f9eacbd5b569941d381a29000000000000000010f9eb50ab7139969d381a39000000000000000010f9ee1c6867df992d381a49000000000000000010f9f00a123f18e02d381a59000000000000000010f9f2a90ba827002d381a69000000000000000010f9f53a35c448002d381a79000000000000000010f9f5b47c06f1586d381a89000000000000000010f9f62b8a5cfa02ed381a99000000000000000010f9f6a298b302ad6d381aa9000000000000000010f9f71921130e1ced381ab9000000000000000010f9f8d9e1e6b3cbed381ac9000000000000000010f9f94d5dc89ca06d381ad9000000000000000010f9fb07b068fd672d381ae9000000000000000010f9fb7aaebcf5132d381af9000000000000000010f9fbe318b7f5902d381b09000000000000000010f9fd22abf5be772d381b19000000000000000010f9fe902d7f029bed381b29000000000000000010f9fffd96ac9b97ad381b39000000000000000010fa014fdae8ee07ad381b49000000000000000010fa02b7e2575467ad381b59000000000000000010fa03127bff9fa7ad381b69000000000000000010fa036e772f9c37ad381b79000000000000000010fa03c79cafd707ad381b89000000000000000010fa03e62bcbe787ad381b99000000000000000010fa0417eecc98c7ad381ba9000000000000000010fa0459a635cf47ad381bb9000000000000000010fa04b3d169a8ffad381bc9000000000000000010fa103254e9d4ffad381bd9000000000000000010fa0546f19677ffad381be9000000000000000010fa10a62bb6dc3fad381bf9000000000000000010fa05d8814b49cfad381c09000000000000000010fa1137bb6bae0fad381c19000000000000000010fa066a11001b9fad381c29000000000000000010fa11c94b207fdfad381c39000000000000000010fa06fba0b4ed6fad381c49000000000000000010fa125adad551afad381c59000000000000000010fa078d3069bf3fad381c69000000000000000010fa12ec6a8a237fad381c79000000000000000010fa081ec01e910fad381c89000000000000000010fa137dfa3ef54fad381c99000000000000000010fa08b04fd362dfad381ca9000000000000000010fa0907e18151efad381cb9000000000000000010fa0ab95a73db6fad381cc9000000000000000010fa0b3a71f995e7ed381cd9000000000000000010fa0b85444cd177ed381ce9000000000000000010fa0bc8544ce7c7ed381cf9000000000000000010fa0be6e368f847ed381d09000000000000000010fa0da00613b250ed381d19000000000000000010fa17920e517190ed381d29000000000000000010fa0e1f5349fbe0ed381d39000000000000000010fa0e626c3ae3b0ed381d49000000000000000010fa0e80fb56f430ed381d59000000000000000010fa0edb94ff3f70ed381d69000000000000000010fa0f37902f3c00ed381d79000000000000000010fa0f92255f1e80ed381d89000000000000000010fa0fd53e500650ed381d99000000000000000010fa0ff3cd6c16d0ed381da9000000000000000010fa10125c882750ed381db9000000000000000010fa105020e62830ed381dc9000000000000000010fa108c3db36400ed381dd9000000000000000010fa151fa23abc406d381de9000000000000000010fa1651395361406d381df9000000000000000010fa1782d06c06406d381e09000000000000000010fa18b46784ab406d381e19000000000000000010fa19e5fe9d50406d381e29000000000000000010fa1b1795b5f5406d381e39000000000000000010fa1b4f461779ea6d381e49000000000000000010fa1b8510e727939d381e59000000000000000010fa1bb9b2f910fd9d381e69000000000000000010fa1beb27be991d9d381e79000000000000000010fa1ccab91d92881d381e89000000000000000010fa1cf88fc7ab481d381e99000000000000000010fa1d260509c4c61d381ea9000000000000000010fa1d52d21bb70b9d381eb9000000000000000010fa1df954bfc1519d381ec9000000000000000010fa1e92204c13d19d381ed9000000000000000010fa2138bd7c26119d381ee9000000000000000010fa21574c9836919d381ef9000000000000000010fa2175dbb447119d381f09000000000000000010fa21946ad057919d381f19000000000000000010fa21b2f9ec68119d381f29000000000000000010fa21d1890878919d381f39000000000000000010fa21f0182489119d381f49000000000000000010fa22332c9d08219d381f59000000000000000010fa224fe65621e19d381f69000000000000000010fa226b18e76e019d381f79000000000000000010fa22864b78ba219d381f89000000000000000010fa22a02708cdd87d381f99000000000000000010fa22b9d563b3707d381fa9000000000000000010fa22d1f9a564507d381fb9000000000000000010fa22e781791d3cfd381fc9000000000000000010fa240a8e4261bcfd381fd9000000000000000010fa298fdde756fcfd381fe9000000000000000010fa245138a43accfd381ff9000000000000000010fa29acd39a98ccfd48201009000000000000000010fa2495cd308c4cfd48201019000000000000000010fa29e4838b788cfd48201029000000000000000010fa24d9bcb5181cfd48201039000000000000000010fa2a17561f055cfd48201049000000000000000010fa251cd12d972cfd48201059000000000000000010fa2a5974b9032cfd48201069000000000000000010fa255fd95af62cfd48201079000000000000000010fa2a9c237e332cfd48201089000000000000000010fa25a2dd0fec6cfd48201099000000000000000010fa25ab97317c0cfd482010a9000000000000000010fa265219d58652fd482010b9000000000000000010fa26f20ab4636efd482010c9000000000000000010fa27839a69353efd482010d9000000000000000010fa28131b5c7aaefd482010e9000000000000000010fa2819ceef6bdc7d482010f9000000000000000010fa289b5ba245507d48201109000000000000000010fa289ef86662107d48201119000000000000000010fa28a208c282a87d48201129000000000000000010fa28a517121de87d48201139000000000000000010fa28a82561b9287d48201149000000000000000010fa28c6ccd2e5c1fd48201159000000000000000010fa29206a56caa07d48201169000000000000000010fa2920ff64d3a87d48201179000000000000000010fa29283271e2d1fd48201189000000000000000010fa292880ad0bf1fd48201199000000000000000010fa2928bfe95c70fd482011a9000000000000000010fa2928e52cdeb2fd482011b9000000000000000010fa29290a247374fd482011c9000000000000000010fa29292424c585fd482011d9000000000000000010fa29293d744ff97d482011e9000000000000000010fa292956c3da6cfd482011f9000000000000000010fa2929701364e07d48201209000000000000000010fa29297c7291c07d48201219000000000000000010fa292987cf6bc47d48201229000000000000000010fa2929911190623d48201239000000000000000010fa29299984d67abd48201249000000000000000010fa2929a1cfa11f7d48201259000000000000000010fa2929a9eadf867d48201269000000000000000010fa2929afaade947d48201279000000000000000010fa2929b3e2555a7d48201289000000000000000010fa2929b819cc207d48201299000000000000000010fa2929bc5142e67d482012a9000000000000000010fa2929c088b9ac7d482012b9000000000000000010fa2929c4c030727d482012c9000000000000000010fa2929c89585267d482012d9000000000000000010fa2929cc6ad9da7d482012e9000000000000000010fa2929d0402e8e7d482012f9000000000000000010fa2929d41583427d48201309000000000000000010fa2929d7ead7f67d48201319000000000000000010fa2929dbc02caa7d48201329000000000000000010fa2929deb3cc9b7d48201339000000000000000010fa2929f0f62897fd48201349000000000000000010fa292a033884947d48201359000000000000000010fa292a0c257a0afd48201369000000000000000010fa292a13f8315afd48201379000000000000000010fa292a1a6546f4fd48201389000000000000000010fa292a1e3a9ba8fd48201399000000000000000010fa2c01ecc2b4b8fd482013a9000000000000000010fa2e6343f8d900ed482013b9000000000000000010fa3074539cba5f4d482013c9000000000000000010fa371422514d32bd482013d9000000000000000010fa3714708c7652bd482013e9000000000000000010fa3c9826693ed2bd482013f9000000000000000010fa3c982f7a9b60bd48201409000000000000000010fa47c275b7dc407d48201419000000000000000010fa515a03ce8fd6ed48201429000000000000000010fa515d78d62886ed48201439000000000000000010fa5435475e4196ed48201449000000000000000010fa570d15e65aa6ed48201459000000000000000010fa57da5da4190d6d48201469000000000000000010f153c646821f680f87cc56783282094c6818a83282095c681bd83282096c681bf83282097c681c183282098c681c383282099c681c58328209ac681c78328209bc681c98328209cc681d28328209dc681fe8328209ec78201008328209fc7820102832820a0c7820104832820a1c7820106832820a2c7820108832820a3c7820146832820a4c0ef944945ce2d1b5bd904cac839b7fdabafd19cab982bc0c0d5d482012d9000000000000000004c3e99215cf0d8c9c0c0da94497218097ed364385356d84e7c289b6f10b82919c0c0c0c0c0f5944976a4a02f38326660d17bf34b431dc6e2eb2327c0c0d4d381ef9000000000000005bcb28603d5865487b3c7c681ef834e6757c0ee944988c9c03950f10b14bf7e8bee5d5a4ede8c45b7c0c0d4d381909000000000000000000007f7e759e9ff16c0c0f29449e57033e27ed548fc5eac37a49f342869e45545c0c0d4d3819290000000000000000000293fdbdc68b3b4c4c381926fc0ed944a97cfef367ceaf890470d7a3095e2c4a5f8a09ac0c0d3d2779000000000000000000592febbd2acbd98c0c0f0944b8586be71d5e400cf5fd768fd206bb179815c24c0c0d3d20c900000000000000000044695cb7570ddcbc3c20c1ec0f4944b95963192df7c7e5fe2c017997a7c281a3be2cfc0c0d5d482010f90000000000000000000105cd6900576bcc5c482010f29c0f6944baa5b44700e0d6360933ee6998c2a6f44a6f4afc0c0d5d48201199000000000000000003e4cedfe4d13983fc7c682011982d5aac0f89f944bab2cb2ed6ee2d564ba5398cab4161d95e58f1bc0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000004a0d771858cdfbf142190c9fa275a8300d7c88cd19b5988d815e0f7dbe4069c0400c0c0c0f4944c08704798dfea887c5687e309de82bfd87950cac0c0d5d482012c900000000000000000000013e4322af7e0c5c482012c02c0f0944c56862612828498c21ed2d534da9687da57bc68c0c0d3d25890000000000000000000287c1452fd4b81c3c2580cc0f885944c9edd5852cd905f086c759e8383e09bff1e68b3f849f847a0b209b3d48408fa1096c0b2d98ecd4a8e873a5efd6855d2bbc45eb4d65831aa90e5e4820123a000000000000000000000000000000000000000000000000a2f7f50f6d5d40000e1a0396a2330c3e96731d20b554a4cd7844bd51ef95f0419f3cf9913a09b68863984c0c0c0ef944cd00e387622c35bddb9b4c962c136462338bc31c0c0d5d482011090000000000000000001208b8193fcc7d1c0c0f90375944da08a1bff50be96bded5c7019227164b49c2bfcf901aaf845a00000000000000000000000000000000000000000000000000000000000000008e3e272a0dc686571248e88c9209777e8caf9e46b9a3bb776459bfcea25d009143dd22060f845a00000000000000000000000000000000000000000000000000000000000000009e3e272a00000000000000000000000000000000000000000007bbfd294e8569419c7470df845a03767beb2f170e71264fea18f450de2fa4e162ab243858f39c42ac274c4ae344de3e272a020e042aaf468766fa2d19d14c7531d4aeaa633fbe66c219785b9e4d6000fc817f845a055e0f83a73febb53fb1fe91294da54100ec98355bf853cc50ce134b8d3907b02e3e272a000013ad74fcc67116b4fd08fb2a67d21938051b12c4f26795cb9c82afbfef3a2f845a0a3913c801a5a2004ae510df5a3fe05727ea86bd445df7d6c30f1aa88c5958711e3e272a0002765c08515f933c70647665f51ca4bbe23c0e073df8bec544286a5f9782696f845a0ce21d460b235ccf8881889e96cc980faa97543599c2f65bacf9346d98ed42048e3e272a0ffffffffffffffffffffffffffffffffffffffffffff3561ff7b5cc9b0084c17f901ada00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea0000000000000000000000000000000000000000000000000000000000000000fa02b789ff1281ebcfed0ef8889d9eec892a26e2df3ec6e40596c55391dc6da3948a050090274db5ce8b33350f708ef273c856e357a7dad54bbfaf7c356fe86ae01f0a092673577f1dec7fd1a60ea85672b9ab1e3c76200998b04af0bafb78b04db0b2aa0c677d98febed3a2933ad0474d2250dc0fe0d2f66b65a981ddb4515207448812ca0d3816fca9c5c37ad2415843fe0786962f569eb3ca39ddef9d5e65493cde8e90ba0e18a12436aba97254a10178d9b72b85c81071917a5628a2d652c1e2ecda2d396c0c0c0f4944de787046fbc43377208a020aa7cb8cf6b6bb31bc0c0d5d4820123900000000000000000003276a87094633ac5c48201234fc0ee944df817c2be2c1645226e6177f4efef2eff3661dcc0c0d4d381de900000000000000000033f4392008bc000c0c0f901de944e033931ad43597d96d6bcc25c280717730b58b1f9011cf845a0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff17e3e208a000000000001e6873f8c3339e937398400000000003462ad45178006aa8f3ad06f845a0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff18e3e208a00000000000286a37e6c1d74d737d44d50000000003588ff0d7ef131fea0d22bdf845a0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff19e3e208a0000000000000000000000600688a119700000000000000000000000000000000f845a0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff1ee3e208a000000000000b2724849870a7759117ae00000000000000a07037b267b7152517f8a5a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff16a0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff1aa0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff1ca0fd2dab4be6d07bba0109696859cf3ea9f610b92569d2a062e705af4b9c58ff1fc0c0c0f902eb944e502545debffbb0f20b9793d7c7d38b3f0f877df90120f846a032e6ed36d6136973cb3b1827dfe925c5089ca7c5e3df30a8474f27fcff44b52ae4e381dda00000000000000000000000000000000000000000000000000000000000000000f846a094c7885a7d14a580346d330f5dae0d609fbf3c0a3fc9ad60894a5d924377750be4e381dda0000000000000000000000000000000000000000000000000002c23872eddb87cf846a0a81db9717717012aa1d211cbfdca77b24437e69f83de246dac7b0b24390ddd86e4e381dda0ffffffffffffffffffffffffffffffffffffffffffffffffffffae9e63995252f846a0faeddf7f426ac4da780ca6b94481c6c6e54f8a68842d18a05c815c8428586612e4e381dda0ffffffffffffffffffffffffffffffffffffffffffffffffffff86f8cdeba4edf901ada00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000015a00000000000000000000000000000000000000000000000000000000000000018a018941ce65cc1c52c15a23e526e4c35e14179c4d45da8127b5f3b5e1e6dce12a3a04c940f44f7daa735b43c25d09e30c001f0f658dae546ad67beb0f7bf0f38c1a6a063410be2035d9fe6d79863091d9e7405f927d9343a7c7a8f08cf36b1f25c6b5da0ada9f2f180bbfb702a0feafe0810fa96879835ca0ae4f7e7e81eba129f0efc98a0feaf537cfef56ffbc04ac5e9b458b9367508657de833f755f8407fc9261f1d77c0c0c0f7944e5b2e1dc63f6b91cb6cd759936495434c7e972fc0c0d5d482012790000000000000004897b01c6bb07de581c8c78201278310a586c0f8ca944f4495243837681061c4743b74b3eedf548d56a5f88ef845a03405731bebdbcb79e500db48ac8e016c3ba57fa37dc2520a3416b70b428e8b2be3e217a00000000000000000000000000000000000000000000000000000000000000001f845a0a61e3476bec9160520db11b0671e68af7b3828e5ecb8bff66ccac312da2d933be3e217a00000000000000000000000000000000000000000000000000000000000000001e1a011141f466c69fd409e1990e063b49cd6d61ed2ecff27a2e402e259ca6b9a01a3c0c0c0f4944fa628f0d1759a6f5d38d8ffd6a2d39e5800774dc0c0d4d381fc9000000000000000000086cfe80a6092fac6c581fc820102c0ef945001e8ee78f3c7366c23053e12110869ed70c51dc0c0d5d48201389000000000000000000369bd787caf3f79c0c0f90154945013b8c94ff2e95e8b36e3c064274b20987403f8f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e272a0688a11970000000000016ebf91362eb963460000007b61d9607246939da5e949f845a00000000000000000000000000000000000000000000000000000000000000009e3e272a0000000000000000000000000000000000126a4eb29dd8eb96d7ba0cb092e7422f845a0000000000000000000000000000000000000000000000000000000000000000ae3e272a000000000000000000000000100a397c974dd08d10832790adeeafd24b95b8ea2f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f90333945018be882dcce5e3f2f3b0913ae2096b9b3fb61ff901aaf845a00000000000000000000000000000000000000000000000000000000000000002e3e251a0000000000000000000000000000000000000000000000000000008f50f675d0af845a00000000000000000000000000000000000000000000000000000000000000003e3e251a00000000000000000000000000000000000000000000cef77fe89dec6d5c58345f845a00000000000000000000000000000000000000000000000000000000000000011e3e251a00000000000000000000000000000000000000000000000000000000007d41a93f845a0000000000000000000000000000000000000000000000000000000000000001ae3e251a000000000000000000ddf40182660ce6300000000000000000ddf385c53eb0b60f845a00000000000000000000000000000000000000000000000000000000000000022e3e251a000000000001529a7d58eec9cba5a59ac00000000001514c5d79d3b3e673bc9cbf845a00000000000000000000000000000000000000000000000000000000000000025e3e251a0000000000000000000000000688a1197000000000000000000000000688a1197f9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000019a00000000000000000000000000000000000000000000000000000000000000023a00000000000000000000000000000000000000000000000000000000000000024c0c0c0da945141b82f5ffda4c6fe1e372978f1c5427640a190c0c0c0c0c0f29451e413ac811eed825fffc2dec3f5e576e8dcf82cc0c0d4d381eb9000000000000000020224112c3ce08380c4c381eb2bc0f4945297715b9fd3ca8074f66931c7d2849203a7425ac0c0d5d4820118900000000000000000002355121cd008a8c5c482011817c0f902929452aa899454998be5b000ad077a46bbe360f4e497f901f1f845a00bb8667e1ee6b6e6118785dfbdc2fd4ded28cfca648afcf3bad9572eece212d9e3e208a000000000024422000e104e20d114232e000000000000000000054ae7e52e7001f845a0152a69092738c066daa08b8777778da6b057279be14ca7d1d49b640d77c39157e3e208a00000000002314a000e104e20d114232f6ce94cab85e25a376ce94cab85e25a39f845a0682167f03d2f86443da5d0f51d2ca8859c50078b4cd1c8ebc64109e9b18631e6e3e208a00000000000000000aa10bb486fef681e0000000000000000b890b0d39efa9c1ef845a08871f11c8fe6bd1f8deafc6624e239df84ab1d8dcc20834245629b415f9a1b3fe3e208a000000ff88f9822000e104e20d114232e003fe00000000000002ebdb7d4e57c01f845a0a1c9baadae9605c2d80ba1bb7f78050c6c3bdf08337ca0849c9b62b20d1a5e00e3e208a000000fb92f894a000e104e20d114232fc480000000000039492e827fb940b237f845a0a8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf21e3e208a000000000000000085805efd0200000080ff7177f91a228465c01e86c43e80277f845a0f942f4688cdba65adc8aa59da583acae93fa87351143ebc775559218bfa5f832e3e208a0000000000000000000ceeaa10a654a00000000000000000000f8453270690200f884a00000000000000000000000000000000000000000000000000000000000000001a00ddd0a1faca9fe65b00c7ddd2626a1f25bf1f1e7f2f7a3e465a43e3fc77db927a0ad967e153ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0c54ea4dd6a734fa461b7ee355eb25a8cb9467ce4d0e78fa68c9c085fda8edba2c0c0c0f294532682ff559666d62ff928d7b57c334fa9e84de7c0c0d4d381a29000000000000000000000000000000000c4c381a206c0f494533d2ab03e309ad73be9bcd80b298cb41c6db142c0c0d5d4820125900000000000000000000ca0f18b7254d4c5c482012501c0f85d9454586be62e3c3580375ae3723c145253060ca0c2c0f842a04b52de242af8e0998837b071693bd8f6ce57e6a143f73600341bde1d27dd942ea0c6521c8ea4247e8beb499344e591b9401fb2807ff9997dd598fd9e56c73a264dc0c0c0f09454869c9a52baccc9f987c50ed238441a9841ba6ec0c0d3d25e900000000000000000009f7b91d1f7dd67c3c25e0dc0f09454975a68c0609280f67050f3b167d554e89c325cc0c0d3d27e9000000000000000000007c49ecf2a793ac3c27e0ec0f85d9454a8757c2fef8649830b158a8c19d3a670e80318c0f842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f794555ce236c0220695b68341bc48c68d52210cc35bc0c0d5d48201429000000000000000051fc859e118620649c8c78201428307e5b2c0f9010294556b9306565093c855aea9ae92a594704c2cd59ec0f8e7a0000000000000000000000000000000000000000000000000000000000000000da00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000011a032b8c3e0163a3eceb0dd8f4a69ff6a8f786c1f0b2824e29def2a833031a4d374a03e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a31a08819ef417987f8ae7a81f42cdfb18815282fe989326fbff903d13cf0e03ace29a0f68cd560cd5285fa96f176cf0d55ce2f05178e0b25aac465f04891d21bfb0c8bc0c0c0f84c94559432e18b281731c054cd703d4b49872be4ed53c0c0e6d21e90000000000000000377de7d2d5cd147dcd21f90000000000000000377ddbbd5f040b63dccc51e83077f0dc51f83077f0ec0f29455a28a0334b507a3bf827a2050fde1519139ad70c0c0d3d2239000000000000000001fe58d74143a428bc5c423826c95c0f79455fe002aeff02f77364de339a1292923a15844b8c0c0d5d482012090000000000000000e123525a7ad4d597cc8c78201208314f291c0f39456de1961fda5454e6f8e6d0e3124ff648fd69400c0c0d3d253900000000000000005485cbf2a7b99da99c6c55383027832c0f86c9456eddb7aa87536c09ccc2793473599fd21a8b17fc0c0f83cd381d89000000000000004955dd44b95d8ba40a2d381d99000000000000004947891f93e6f3b534ad381da9000000000000004947752bcda416765f2d5c681d8837ca5cfc681d9837ca5d0c681da837ca5d1c0f87e945703b683c7f928b721ca95da988d73a3299d4757c0f863a00000000000000000000000000000000000000000000000000000000000000065a00000000000000000000000000000000000000000000000000000000000000097a077041d625e83eec30f111a00cc41237389024e39804657e1d8c85ac038f24dbdc0c0c0f494571f7be0d706e84bfc902fb503f668f73d97636bc0c0d5d482010c90000000000000000008a35be45f0867dbc5c482010c28c0f842945724ff60178dbda504cda6759ebf54a16146141dc0c0e8d381f1900000000000000008eecce29ebbdc2255d381f290000000000000000aaae3b913a85c2255c0c0f9010d9457ab1e0003f623289cd798b1824be09a793e4becf88ef845a04691f66de7d80ce42a081aa4bd60fb51aabcc0c52150ebb3eaccf4ffeb2db73fe3e251a00000000000000000000000000000000000000000000000000000000000000000f845a07c68612ac0fbdb3238c22e90b72e70b9964f9c124d83372937aaf35501f86269e3e251a000000000000000000000000000000000000000000001e308032fba4d36524372f863a034585ab80ba1df59a989f3ef0a7d93fb0e76402b62389d18ccbca9dfe77bb036a0a3f5d68cb14a82c6ebcf0673f80f5f00cf5479cc58f9d91e709d98b9088ff445a0de7fd8cf191180ddca506141632dde4c47d40344b3edcd10f1d74c93da232c9fc0c0c0f901c89457e114b691db790c35207b2e685d4a43181e6061f901acf845a006694443be61d3bead28ad564139d8813adce3e1cc7bfabe502d0df2c8325df1e3e22fa00000000000000000000000000000000000000000004ee54fd6aaeb13b0f21637f846a042c3c975aef34537203fe419bd3d01ced0b59024258736cdd687a16eaef719e1e4e381cda0000000000000000000000000000000000000000000000bff3280256a6e8f0000f845a097c61cf25a75e9ae063e7f086838f5736319e97f8e04cdbe66d8130ec48cfa79e3e201a0000000000000000000000000000000000000000000037e5cf33515f9e48fe62df845a0a37b0e3adbdb184815c07baddfda6fefa100f864754f985fcbe24f44178bb025e3e201a00000000000000000000000000000000000000000000002ab8fc7b5cc30dbcd74f845a0b138da85d4a6bc6d9df0416684b5310719fcb67b92e491f770223e14366bbe60e3e22fa000000000000000000000000000000000000000000000006db0b7e2e9e7e08000f846a0df70affd7af96966d2670c0f3ed33a9da6b177986dc23d985c9fbdd265f954a7e4e381cda00000000000000000000000000000000000000000000d8464fac9eae21919b800c0c0c0c0f8c094580ee6b001348fa0deb675f4d55259c96c4a3a31c0f8a5a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000008a063187d71e139eee983a88d0737447c7451979b3dbb75903c76b5fe430d36588ec0c0c0f494582a7549a02c8e8ea68aed17d5b638da616ac3fec0c0d4d3819e900000000000000000070e62d7ab243875c6c5819e820330c0f8c0945857feb095302407a718ba6386a53c35da0deddfc0f8a5a05e155a97eb175a88171e46c32490e27b233d3f88cf417d8c63adbae4a9953ab2a0c5d97fac1fb086771aed8e18097f4a592ac1d148017b2d4abf3c362c10779e02a0cc2c919a9936f62868368e517bb4f3602dd0536094fb620e6a8aa11a373ff033a0dc4540db8f0963570faf41f59ae94abe06c7fa67e9c4153c5704dd20bbe1fb51a0e6af6d2ae335a90de767f2f5fd24405f8a29336d7c37a63d0df3b582130a2a08c0c0c0f8ec9458aea10748a00d1781d6651f9d78a414ea32ca46f88ef845a052bdb5b93751a403e782f61a949614bd7cc89ef403e33deded79eebec6239c1de3e273a000000000000000000000000000000000000000000000e621b6e5fd6c94d92ce4f845a071e19aa0420ec31c5cf3b7de7d806bfb695520bbc3beb3416e6d09f1f045289ee3e273a000000000000000000000000000000000000000000000022512f7b2a0d73a0ae0f842a006afade4f2eae53ba4bcd503fa4c80b231de2aba72f5cd0019688695d56bb105a067495bebbcea6b2f8ff22cc5a8ff1151ac065b75f0f9556d64b186e9f5fc47e0c0c0c0f69458c42647eb2cee1686c3ca596e31dd69a8c993d1c0c0d5d482013f900000000000000000aad9e19e01ffc24ec7c682013f825280c0f39458edf78281334335effa23101bbe3371b6a36a51c0c0d3d25f90000000000000000267258d9876e376f7c6c55f830b8469c0f3945b43453fce04b92e190f391a83136bfbecedefd1c0c0d3d2019000000000000000065996dbef1fd3a689c6c50183043c47c0f84a945babe600b9fcd5fb7b66c0611bf4896d967b23a1c0c0e8d27d90000000000000000184578c5890118408d48201129000000000000000018456acd91671158dc8c782011283034d56c0f9010e945c7bcd6e7de5423a257d81b442095a1a6ced35c5f88ff846a0c3ec7e2084dfdd3a494595954ffa85dd2a8b45664068342aacc7574de347f2c5e4e381dba00000000000000000000000000000000000000000000000000000000000000002f845a0d0ef1a95eb5fe93910150115352e8ccf0857ea3cfcfbe8794a49fbdc05d80915e3e274a00000000000000000000000000000000000000000000000000000000000000002f863a00000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000086ba0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f0945d3964e8bd3e4aaa5c86927e3c944cb1c0c4f474c0c0d3d2709000000000000000000000b529f4c0e8c8c3c27012c0f2945d93e31a2235e2c6af10799fc333e97f5b5fdbd8c0c0d4d381cb900000000000000000000212a1dcf58cccc4c381cb34c0f6945d947f367bbb2251d3d2b7e9320424d5d6ba980bc0c0d5d482014190000000000000000004dac290c5bf9a06c7c6820141820240c0f83b945de1b25d0a94549c9dace58777c385fa67aa846fc0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f3945e2b6c6b2240d582995537d3fafdb556e4a3822fc0c0d3d2029000000000000000006a4680cb7e1cbefdc6c50283065021c0f2945e5778536d3338bf6f001f3e2f44d99d5400fe28c0c0d4d38180900000000000000000000ebf2751374884c4c3818007c0f3945ebab6791bb90ac6acc1afa394e440366b933310c0c0d4d381e49000000000000000000006d6c4711faacdc5c481e48188c0f0945f034589713d3a9f4c67a09b478b800db7764cd0c0c0d3d23d90000000000000000000074516d2822081c3c23d05c0f85d945f4ec3df9cbd43714fe2740f5e3616155c5b8419c0f842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005c0c0c0f90232945f64ab1544d28732f0a24f4713c2c8ec0da089f0f901f5f845a00f12b799b59d030df91357dbc659788be3afe85ed8ab31fb27b990a45e64d037e3e20ea0000000000000000000000000000000000000000000001033ee67bc14b0f90159f845a02578ed7667b2e2fae43f52095eadc659b460ddc1f604cb70acdbc3932261f315e3e20ea0fffffffffffffffffffffffffffffffffffffffffffffed54c49e51be710ff79f845a045f0b7fa21a4287c5918668b6ede365097a5a85bd0ca3a042787e67ede5e18b4e3e20ea0000000000000000000000000000000000000000000001728c6940f601be28828f847a062d1ba451cd9bff25ab16da34e80f27dcea3a8871d60843b624dac8e638653bfe5e482013aa000000000000000000000000000000000000000000000937d4b36a4da7f77582bf845a069a1997d5f27fd969be349ea22f3e24ef4e94682f22fa7a1b6e23f01fc312c59e3e20ea0000000000000000000000000000000000000000000000000000000000000001af847a0a5f0144de9db59486366f7b445c5b2533283c0ee862892e9cdfe04f7c6984aa4e5e482013aa000000000000000000000000000000000000000000000038c16b563b32006e252f845a0d7598ba2b8edb81a78e85bd10b6ed1bc2d0c0ec981dbaa7b7b55f301cd407c8be3e20ea000000000000000000000000000000000000000000000703da631a866375521fbe1a021c83849a98c24884fa1ccc35ff710ab3d9f125ff1af7913b10a7defa78f35b3c0c0c0f83b946088d94c5a40cecd3ae2d4e0710ca687b91c61d0c0e1a00000000000000000000000000000000000000000000000000000000000000065c0c0c0da946131b5fae19ea4f9d964eac0408e4408b66337b5c0c0c0c0c0f844946164fb05d6fcf67f7c5ec0d4e013d5895fd169c9c0c0ead482011890000000000000000000e3fd38263b524cd482013d90000000000000000001c55be812cd03a9c0c0f8a594616de58c011f8736fa20c7ae5352f7f6fb9f0669f847f845a0d6515c1ab23c173d3fa75dfb8397d82d6cac6cf050c7d433cbe2fe8ab1c7b17ee3e205a00000000000000000000000000000000000000000000000000000000000000001f842a00000000000000000000000000000000000000000000000000000000000000000a0b24e76a7f8dd0cbaae591c6f267ed91d1658e795c6799def790dc03129e6817ec0c0c0f4946206702c381281e56b128a9d7719b8844d84385bc0c0d5d482013d900000000000000000002355121cd008a8c5c482013d17c0f86b94623777cc098c6058a46cf7530f45150ff6a8459dc0c0f850d381949000000000000000005c7211ef9a484462d381979000000000000000005c72207ce4994850d3819a9000000000000000005c722f0a2eea4c3ed3819d9000000000000000005c723d97793b502cc0c0f8649462c1e9f6830098dff647ef78e1f39244258f7bf5f849f847a03f5e2ad317328859de1d5234a8bd619fe5fdf66d2173998bc4cb0c47f1e90dbde5e4820101a00000000000000000000000000000000000000000000000000000000000000001c0c0c0c0da94634769eb87542eaf41c0008c05d5d8f5d8bec3a5c0c0c0c0c0f9015494637340ffaeec55e9da0efb799f99fdb879c12a3cf8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20ca0688a11970000000000001eac6d08bfd9dae9000000000000005a3cd5ba74498df845a00000000000000000000000000000000000000000000000000000000000000009e3e20ca0000000000000000000000000000001d687dd6515edd5f74ba9af18aab2385800f845a0000000000000000000000000000000000000000000000000000000000000000ae3e20ca00000000000000000000000000000000007cf576938e3cc04f0810de301c2d698f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f90133946378f1fdaf3048ca71d8f69671c31cb83cabee69f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e21da0000100000100010000fdbe46000000000000000000284795e597f0925bdc82f2f845a00000000000000000000000000000000000000000000000000000000000000002e3e21da000000000000000000000000000000000001dc707a2cf8fbf6286217746a5b0c2f845a00000000000000000000000000000000000000000000000000000000000000008e3e21da00100000000000000000000027f8088d894924f237bffffd0f53a21fc688a1197f842a00000000000000000000000000000000000000000000000000000000000000004a0c0d1c00078410fd0164580b0bad93d8a579580d06cf45fc2696a823498097b8ac0c0c0f901759464351fc9810adad17a690e4e1717df5e7e085160f8d5f845a006a9252c6538079518b2b352a6ce6548cf0b6b07fa7b705bdd31a9f476041f68e3e265a00000000000000000000000000000000000000000000000000429056bd1973ec1f845a0c269910e79664bb02e4ea6b58a6546a8f30ea3f5840ed79d34d06b85ebddd776e3e265a0000000000000000000000000000000000000000000000000001fe4f872e101aef845a0fc5da5621ecdf503c1b60d3853c14e4222d580d5bc84c71986837c27fa27ef1ae3e265a00000000000000000000000000000000000000000000000b026959c730dc7ee1af884a00055db1c41b2272eaef9225cd2750f18a33dcfe06f456a5d8bbff9f2785bd740a02a9f100b9103993ec2993a2dbe2dd6ab0ef65a4bc285359a6d2f66ad33120716a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0ee94649a9d2ad1d874acd8dd4feb069db362989b1971c0c0d4d381f6900000000000000000000768ee60903e60c0c0f69464bd83be58924106695ca923b159cbebb20628bdc0c0d5d48201269000000000000000001b0664689d04d821c7c682012682676bc0f29465233016509316a31725521d270985d017605260c0c0d4d381f7900000000000000000000310156c90f777c4c381f702c0ee9465b07a984be4e826fd23969d0b2e45a0eeb25e73c0c0d4d3818d900000000000000000000110d9316ec000c0c0ed946627f796f092b84b406981c1e4ce81f9a4fb43a5c0c0d3d2129000000000000000000001fbe638bc2319c0c0f89f94663dc15d3c1ac63ff12e45ab68fea3f0a883c251c0f884a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca085666f94a26173b9c3e389a3740c029d5e88a5af5fa6b800fbeb55b3ff0317b9a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0cd42f7cfea4a1fa33b3d0afadef76575855644c7d27312e535ac5e13da0e1a81c0c0c0da9466a9893cc07d91d95644aedd05d03f95e1dba8afc0c0c0c0c0f09466b733cd5dc02830c5d4897c729fef517abb8487c0c0d3d269900000000000000000000bf39661519229c3c26902c0da946747bcaf9bd5a5f0758cbe08903490e45ddfacb5c0c0c0c0c0f83b9467a45c5a86ebf8c66958b11b019c6ae44c797f41c0c0c0c5c482011d01dcdb82011d97ef0100411d38d27f6f2c7f3b70ff29dada64cbd7bfa9b2ed946836451238fb831812c3aa45fb81ea19404dde5dc0c0d3d258900000000000000000fd2f8f23ef256000c0c0f9010d9468749665ff8d2d112fa859aa293f07a622782f38f88ef845a03caf690871077112de96bbade7af43609bcff39f4bcd9207ac2e91caa223daade3e216a0000000000000000000000000000000000000000000000000000000000ae124d5f845a0a929bef58f099c4c46db9c22f6056fac18e57926e858af3c5e84dd1a1636c6a4e3e216a00000000000000000000000000000000000000000000000000000000000000000f863a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca05e65dcfd484b35a6aa2a9a0211b40586ac81e38baf9773281f6ed989e7296755a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0ee9468b0ad78d79c7c92a16b33f4a7de900c94aa6f06c0c0d4d381ef900000000000000000000c7741308e34dec0c0ee9469089031dbae3fe96971a8c2566012d9ae2e7250c0c0d4d381f0900000000000000000000176cea201eceac0c0f901119469209dd095f6d835e51f9db68732893ae636ee41f8d5f845a0002d2dfb03405be9a3ac6d76cb83ef7d39f8feddf1f48a6128a3c0c28ce8482ce3e236a00000000000000000000000000000000000000000000000000000000000000000f845a0227fd1fc1d4e46436eca13e12e074e4be5c45dd7e8ce87a9b180cc2da5f1d343e3e236a00000000000000000000000000000000000000000000000000000000000000000f845a022d2c177893fcc717cc52307253660f1a5b11f7e88c9b91361f8127d949f5e60e3e236a00000000000000000000000000000000000000000028bc3f098bc4516643c8e04e1a00000000000000000000000000000000000000000000000000000000000000007c0c0c0f8c09469460570c93f9de5e2edbc3052bf10125f0ca22dc0f8a5a0322c2f1d9209969f334c8955443b224cadc85f453939eb2b4ffb8af019944ecea044629564ae37fcd36b0f7214cc9716ba49370bb164b94e906f723ae0fe6c69bea04fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab2559a0de98b90b1713f5063c3418d1b357cf3b187ddec8b5d652063ec9e3f92c541caea0e465ee32fd52e3c4788da8b0bc9bfc53726e040cab58010f4bf2a85773817a55c0c0c0f901549469c4292fb03abc9ea6efe97471bd9c07ffbfda29f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20aa0688a11970000000000007055c8637af4c7990000000118a6ddaa03dcc885696bf845a00000000000000000000000000000000000000000000000000000000000000009e3e20aa0000000000000000000000000000000000000766e5b3a43553ada8b939a744804f845a0000000000000000000000000000000000000000000000000000000000000000ae3e20aa00000000000000000000000000258b9809b50bb6e213e87761ed9d4b2fdb9aadcf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f87e946a000f20005980200259b80c5102003040001068c0f863a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a0c6521c8ea4247e8beb499344e591b9401fb2807ff9997dd598fd9e56c73a264dc0c0c0f885946a8ee8251a43aa2fdb564d57a6b5df5d4f91b909f849f847a00000000000000000000000000000000000000000000000000000000000000000e5e4820119a00000000000000000000000000000000000000000000000000000000000005d2ce1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0ef946adb3bab5730852eb53987ea89d8e8f16393c200c0c0d5d482012e9000000000000000004563918244f40000c0c0f90154946b175474e89094c44da98b954eedeac495271d0ff8d5f845a00000000000000000000000000000000000000000000000000000000000000001e3e208a000000000000000000000000000000000000000000c141e5b8914b4bcfb661035f845a00e3d19729328f478ffc901c115f05d0195e5b68e282b84da93c6bafd953fdc80e3e208a00000000000000000000000000000000000000000014522188f60a0a649f90000f845a031adef62206227419133dd9a6b4041532c22595206a596cf74f19493bfc8f368e3e208a0000000000000000000000000000000000000000000000043a1f911d441537ec0f863a02c0119782c3461cd19a69469612191780a22daff93839018912934cb127d9581a0848ae903f6887b5d0bca5bf7f90c9bbed50dcdc8354dd3e207a5ea769fe48a2ca0d556473391dfdd4316b55e4e1ced572841875f0c55cffcc5f9f3e87c1d988d3bc0c0c0f3946bf97afe2d2c790999cded2a8523009eb8a0823fc0c0d3d2079000000000000000007a7f4144629ba3e3c6c507830157e0c0f90145946ca298d2983ab03aa1da7679389d955a4efee15cc0f90129a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000006ea0000000000000000000000000000000000000000000000000000000000000006fa00000000000000000000000000000000000000000000000000000000000010008a0842abfc34bb944881d81341dcd1e6de46da5c5989c26bece76592f9f0c2056c8c0c0c0f2946cd66d90fd1d90f7c58c0027cdcf31448a9d7d0bc0c0d4d381df900000000000000000000af51b403513e8c4c381df08c0f4946cde44c54a5dcefb86363920ebc5ce5f4bc4710ac0c0d4d381b090000000000000000001923ab6c88d3d98c6c581b0820d4ec0f862946d19568a959fcb4211852f6472d3df7b67c6cd54f847f845a0c9e7e710c81fe3c0e1d581bd7eac244732ea2d03be3efe2f6e33ecd72fa390b0e3e269a00000000000000000000000000000000000000000000000000000000000000001c0c0c0c0ef946d2fe9c5d142880f3a93fa11f38fe8e0d22728a5c0c0d5d4820145900000000000000000039e4c2c385d2671c0c0f90154946d59b6c2058114f16e2ed839a92d89a91271474af8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e207a0688a1197000000000000eec30b1bcde32c7e000000001423fe1609d86175aee1f845a00000000000000000000000000000000000000000000000000000000000000009e3e207a000000000000000000000000000000000027b3c3f08aef6aeac177eec139d9d54f845a0000000000000000000000000000000000000000000000000000000000000000ae3e207a000000000000000000000000000076ad8dc28b9f7d42deb125d20f8ec39b6f12cf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0ee946d5a597129ad5f55a03d2e33c5e965a9cb9450bcc0c0d4d381df900000000000000000007f62bfa6071578c0c0f901de946de037ef9ad2725eb40118bb1702ebb27e4aeb24f9011cf845a024451c947f191ba0b530f793257e0d96a35841b777a0fe4fcdd85e4f88753b98e3e201a0000000000000000000000000000000000000000000001139d334fc8ac34e8db8f845a082925f70a42a8633fa28e1d369e47cc833e0a19d22b9450795322233e965ef3ee3e201a000000000000000000000000000000000000000000000440cb9373851f7f36bddf845a095eb8b8ef7069f8b5c679fa94eca1f6d19856b88aa03f8eaa9ca2b675d797698e3e201a000000000000000000000000000000000000000000000053e9cd22074e537d5a7f845a0de3396827fa575355c40f2591e90c244df1fd101730e44da9e54c1d040587739e3e201a00000000000000000000000000000000000000000000012245bc54fade1d07e26f8a5a010d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390ba02ab7b5d3a006a03e38412a2b87a8a91e6939d7831cfd5517a58d87e84041330ea07050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3a075d179b340750ce57d2ecf17f98f27e78c742b8df016157c225ee4bb188bd639a0d92ed7809a90db8233b3d257c046658f4c9e922961998ee652d620cdfdc796c6c0c0c0f8c0946e4141d33021b52c91c28608403db4a0ffb50ec6c0f8a5a043c8f2d2647a5364f7b9d60f7457f48e15dc0fcee12fdafb7946de2931cd3de8a08b66d076df2dd6688722f6cdac8c0fc9b6fc751fcb1e4dd762ec011ebffb8730a099cd62e62486ae867831c2c0011b576c017cb6aabcc65c7f3a0f4ff306f3ab1da09f3ab2fdd07f5dfd9349894140475205652787be63392a23d141589f7f34ebd1a0ac2f39fb2f41b5739dacad3513735e479a2afcaa35de82131596b12966965028c0c0c0f2946f05b59d4e58e821f48e67d5962f82bf4df1c9ffc0c0d4d381e29000000000000000002c60e982581802a8c4c381e201c0ee946f443e96dd5d6a29be066b5887357e2ba8c8c0b1c0c0d4d381af900000000000000000255cf2670333a127c0c0f2946fa67393be5d9a5b6b3570646ef238bb52795350c0c0d3d23a900000000000000002912b68a75e4468c4c5c43a8261f2c0f29470167b76543c4a12b49b2f2b70cbf04d99345786c0c0d3d22f90000000000000000135356b731358e576c5c42f82093ec0f90154947069f8cea562778bde0bac8f512d3f6b73e1e7f4f8d5f845a0262bb27bbdd95c1cdc8e16957e36e38579ea44f7f6413dd7a9c75939def06b2ce3e26ba00000000000000000000000000000000000000005b71182d9048ec8fafb3f138bf845a052c54c7d408b9ca1cead7ed6a491323e5451549d9ef0992d42f595e36ce08f77e3e26ba0000000000000000000000000000000000000014b1008247b2695eec690f74782f845a0d722500f1a1a9b3f1ad91802a20b180ba05ffe232f9a17da4eddda61a4632ac2e3e26ba000000000000000000000000000000000000000001898faa1d0e7c15f6fa16aa7f863a00000000000000000000000000000000000000000000000000000000000000002a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f094706b46919914e1208a10473f411fc65095e582dec0c0d3d250900000000000000000000600047e5f6881c3c25001c0f85d9470cbb871e8f30fc8ce23609e9e0ea87b6b222f58c0f842a00000000000000000000000000000000000000000000000000000000000000066a0302232516c31bd2939bbdce153021eb76453892c0c2585337fa9076ae9576614c0c0c0f49470fb47c51e443e3106625fbbe839b6b21aa9f09fc0c0d5d48201349000000000000000000002624cefa45907c5c48201340ec0ee947137c38e1b31ea9a2ef59cf7179a2adc4a8729acc0c0d4d3819e90000000000000000000034c26f436ec18c0c0ee947273e0c112517de2d174f0786ee090f5c30fab7ac0c0d4d381ee9000000000000000000078c8f1981eceb0c0c0f29472d4a8e80319e7380acf5fe87b3a218e99cdcecac0c0d4d381f69000000000000000000000004aa91ad198c4c381f602c0f39472e5263ff33d2494692d7f94a758aa9f82062f73c0c0d3d26d900000000000000003718d4ffffcbb70dbc6c56d8306683bc0f8ca9472e95b8931767c79ba4eee721354d6e99a61d004f88ef845a0000000000000000000000000000000000000000000000000000000000000003ae3e230a0000000000000000000000000000000000000000000000000000a98d68f4bb169f845a0e87579d97cad2f208f65e0e8103dc581de8b3fa3d6afc16ee30039101baf1cc4e3e230a00000000003d32fca4546a285774eaad000000000000000000000110dfbe3ac29e1a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f29472f36389a2fd387d0f7e72051f2537bb5cf7990fc0c0d3d25c90000000000000000007aed6992e71a8aec5c45c82082ec0f29474b5c6bb8d946dabe97c537fe78c0e3643baa103c0c0d4d381ad900000000000000000001c39c6dd24bcf5c4c381ad09c0f2947506ad2445690f3046a147991f585afa073d696fc0c0d3d2719000000000000000000000000000000000c5c4718202f9c0f901339476665642f513aaf2a00be05711a598f44e3970a7f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e202a000010000010001000004907c00000000002fba628913a211d8466e15c3064908f845a00000000000000000000000000000000000000000000000000000000000000002e3e202a0000000000000000000000000000058e8e22ba81549f1f667d671fbb74b82d439f845a00000000000000000000000000000000000000000000000000000000000000008e3e202a00100000000000000000369b00e7a7dd8b3a219165b00010ade2e83f4688a1197f842a00000000000000000000000000000000000000000000000000000000000000004a0224033bf7d20523e17d8bf9fe0c779d691193b8ae991a81edb5a115c29808a33c0c0c0f9010f9477146784315ba81904d654466968e3a7c196d1f3f890f846a032d3054900052ea18fe8954b67e1b5f4e5cc5405181cb5704ef34a458e834520e4e381b7a000000000000000000000000000000000000000000000035a3cd59f1c88010000f846a0c2a4f35c1894d1d151069ca483cc639a905a92f57c0e20e6a2a8aed52f731c29e4e381b7a00000000000000000000000000000000000000000002e826cda50079371bbbd2cf863a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0ad3074f3b9db4c8616e5e0839d423e4dafccac8645228abfd7638455b345c6eda0e90c2d9fcdf1569e25899c5c30e83f418710c1e8284fa4620f8d7aa38b6696fcc0c0c0f29477b2c0784794f28582232e6c73932ae6d1cea4bac0c0d4d381ae9000000000000000000024d5ca79fed1e1c4c381ae01c0f85e9478d250172dad653ae5b8cd9b825de8e0caac2c94c0c0f839d2139000000000000000000a252c60b0ff4022d2149000000000000000000377440aeebf7b8ad215900000000000000000036ff14e0795e8f6cac4148204ebc4158204ecc0f29479538bf55bb10715a440d7cf1f809e1cb8fcf3d3c0c0d4d381f0900000000000000000011b7f8db81a2fb6c4c381f042c0f494797fcce0bf1dcf528752f2cb338f0b948c74a01ac0c0d5d482011090000000000000000000003e70bc0da7bac5c48201100ec0ee9479a77a1bf7335dd8dd7b3316a8424d9c90af397ac0c0d4d381e1900000000000000000012a6d8e11220000c0c0da947a250d5630b4cf539739df2c5dacb4c659f2488dc0c0c0c0c0f4947aece52635da73195fc6163b6d250018e6b99c7cc0c0d5d48201169000000000000000000001025bdd0d2f81c5c482011603c0f83b947bbc9768c5ac85f382829c734f88eaf1c9029c47c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f85d947bc3485026ac48b6cf9baf0a377477fff5703af8c0f842a00773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0ee947bfcad3faa7409d61844eb1b7686a31a2d60505ac0c0d4d381a29000000000000000009fd42ad53f53a2acc0c0f9015a947c706586679af2ba6d1a9fc2da9c6af59883fdd3f8dbf847a00000000000000000000000000000000000000000000000000000000000000000e5e4820140a00001000708070801c4fda4f80000000000000000001d22a053233567c9311ca1f847a00000000000000000000000000000000000000000000000000000000000000001e5e4820140a0000000000000000000000000000002d3fef5ee28f276eef401500bfff8c22f84f847a000000000000000000000000000000000000000000000000000000000000001cce5e4820140a0010000000000000001c42813b6a328daa03ecbec86fffc61370a9f1c688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000001cba0ff6987e143035363eecd684681725f471ab8abd92df8eadf4a03ce437f80e0ddc0c0c0f90263947c9f94a79b6b859dfb1d3312fc2311b39f89c677f8dbf847a021694ce8c2469f7002f4eff1ec246e47d9a4df409d03165e44a314c5eabfc02ce5e482011ba0ffffffffffffffffffffffffffffffffffffffffffffffffffd613f667a67ffff847a0671df6dace0af28b1fa15ba77ba2ef3473ec978caa4a238a2a68872648da10eee5e482011ba00000000000000000000000000000000000000000000000010d6ad4c743de45f9f847a0d42ff8000262f173426226e51699ffa2287e636536b0427c4689468cfeea41d9e5e482011ba0000000000000000000000000000000000000000000000000001d21db47288000f9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000011a00000000000000000000000000000000000000000000000000000000000000014a0373991609e5416c4abafff665187355663e47f6712c1c24938357f18f6209b1ca05d51962934d53af948187c9d296d8853c841d6714470bf244e738b103999e10ca06edf5c507081ebffe694b4ac63f611a06793a2d56d719e1a7278228eb5da1680c0c0c0f6947caf266ac3ffb916ade7fb94f965cb42452ffafcc0c0d5d482011f9000000000000000001a3ccb332c986f9fc7c682011f82011dc0f87e947d4e742018fb52e48b08be73d041c18b21de6fb5c0f863a0000000000000000000000000000000000000000000000000000000000000000ba0f5f4b7ea0c109bcb513cfbce575588a57ffd28fdc1df7bab08988234e35ca0cfa0fb44bcdd0398172ec04229ecdf2731caab3b9195751a90735b5969e03b3bac03c0c0c0f8a7947e6027a6a84fc1f6db6782c523efe62c923e46fff849f847a04cf67c78a17bfce7c308c76d72e09d2d09c59c0fedf97f0acc5a40aa4be57211e5e4820109a00000000000000000000000000000000000000000000000000000000000000001f842a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f0947eae43db7afcd5244d6a10209b2778f222b2a433c0c0d3d23890000000000000000001c8fa1564821090c3c2384ec0f4947f06f9a74ed2cf670c8320d5e53c88442dafedbac0c0d5d48201339000000000000000000030721205fac0b9c5c482013321c0f90224947fab4e14e828897c649d2fdbd341688bb0446431f90120f846a00000000000000000000000000000000000000000000000000000000000000006e4e381e9a00000000000000000000000000000000000000000000008970d65c4e029103374f846a0874d03188f0f72622f82b8079e79c30859ab7d6f601d32702617cdbc0c4478e7e4e381e9a00000000000000000000000000000000000000000000008970d65c4e029103374f846a0bd62ba0c1f9014b731e455cfa5dacd23ea7ee805f7bba645eb90e8d67e661e19e4e381e9a0000000000000000000000000000000000000000000a712c2d2f54d7f7551d065f846a0c05d80ee60be459896f795be1fce257ed92856e661742b6c27020a18fa26ad50e4e381e9a000000000000000000000000000000000000000000000408be3aad921ba9a39d0f8e7a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000000aa02ce84882189aca4175fbe260fb4d17ebb462b6ba2b6e29053a9cf24026927efca0aa648bd1a57367eed11f26da60c5d2ca752fe8fd2aa17d8f83a410746396b25fa0b070612fe3f0653f089df472dae55cad389b619ef42af006eb465993ccd4934fc0c0c0f8ee947fc66500c84a76ad7e9c93437bfc5ac33e2ddae9f890f846a0b417fe07be4f12fa0e586f2afc4d165061b10c4a65aafa89cc730b5c05b85edbe4e381aba0000000000000000000000000000000000000000000000000e28df12ab4134c00f846a0dcee52077e6881a673692780ddb6d3689e3e54ed3739777e2c95826a7e607d26e4e381aba00000000000000000000000000000000000000000000000000000000000000000f842a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f294806dd8539ca5be5cacf4dd26829d014141cb1eaec0c0d4d381fd9000000000000000000000b87d76b81e9ac4c381fd04c0f69480ab66f245fd26e5142a1d2a8835400f8f1b059ec0c0d5d482011b9000000000000000000092051447b02007c7c682011b820923c0f902fa94810916a2f5820ae696c34c6e4d2bafc5a4b1da77f90238f845a02a93bec74bfccce482ad341da994c475872b2e6a7991a8a50634edaf76ab69e0e3e206a00000000000000000000000000000000000000000000000000000000000000001f845a0309dd35fc0a466a2ea52797db240a71f633a90b1ce237535b384be3e07416ce4e3e206a00000000000000000000000000000000000000000000000000000000000000000f845a03210768e1ead6fed8d80f20848c72937d8fbf9fc714c11fdb43c5fed3231797ce3e206a0000000000000000000000000497218097ed364385356d84e7c289b6f10b82919f845a05e033968725822e8e540b930e28ad5f45b518194ad0261123b0d4864b653caa1e3e206a000000000000000000000000000000000000000000000000000000000000002e4f845a0a25821d68be1eef419de375b4be0b5a6cc0a9534f460028cafafc36373c56910e3e206a00000000000000000000000000000000000000000000000000000000000000003f845a0b1c5e60e28908bad326ed5638233fc388c12f34a0fdc62ea9eddb37d27a303efe3e206a00000000000000000000000000000000000000000000000000000000000000000f845a0bc878a40eb755143dfedff20e99eeaa4590fcdf92e1651db30a9a080a456a33fe3e206a00000000000000000000000000000000000000000000000000000000000000002f845a0c7e799f3314643c70ed3f6c3d7002938a443ef0bff5d02f4dd43b513f42397d4e3e206a00000000000000000000000000000000000000000000000000000000000000238f8a5a0000000000000000000000000000000000000000000000000000000000000012da0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca04cdc11ac0fec738088f1b94fd91a3abb4ef4fc46d462c8a3ab98e2ed69ed67faa057c34e831b6339d477a7900c1361accaf07bea3374b83acb6e0f2ea078295d61a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f9015494811beed0119b4afce20d2583eb608c6f7af1954ff8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e272a0688a11970000000000084d439be32c3d20950000926e2427f2810ecdf5375e68f845a00000000000000000000000000000000000000000000000000000000000000009e3e272a0000000000000000000000000000000000000d529e534667d446709faa4168835f845a0000000000000000000000000000000000000000000000000000000000000000ae3e272a00000000000000000000508531a38b195f5ad2324cca96530ae1836a0fbcca9d2f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f90114948164b40840418c77a68f6f9eedb5202b36d8b288f8d8f846a00000000000000000000000000000000000000000000000000000000000000019e4e38192a000000000000000000000000000000000000000000000000000000000000016c8f846a086f8800df768673e8d57a3a4ab217bc56c42cdf90c1cdb74fa1ba22cb17b28dde4e38192a00000000000000000000000000000000000000000000000000000000000000001f846a096f4e8ccd696954ad45f78c7d51069e76680215041f7fb23f8724e46e07e78c9e4e38192a000000000000000000000000000000000000000000000000000000000688a1197e1a0000000000000000000000000000000000000000000000000000000000000001ac0c0c0f89f948164cc65827dcfe994ab23944cbc90e0aa80bfcbc0f884a0311ca47c9f29aa77854680a801702c92b0648c49b0c9db20a98bc81682126958a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca04ba33687ddacd057cd141427be2712bc56f5891fc9bca4e09ce17d67f81afc0ba0ad55efbb0ebf9f958bfa260d560c49e9831d25890cd7f7560cbad0d761c0610bc0c0c0ee9481705d166f1978e3368ba6906b98a55a7bdef962c0c0d4d381a09000000000000000000005543df729c000c0c0f294818164f12a47ea39924d1c2ddf1b499425d8fd6ec0c0d4d3818c9000000000000000000000121a98c3de98c4c3818c02c0f0948381f65f5ec4fdd94a5c033fa4261085fbd005cbc0c0d3d27f90000000000000000000110b8bde4b7a75c3c27f0ec0f49483d91529dea3fe969c051e2a04ad99861a979a2ec0c0d4d38199900000000000000000001113c9aded78e8c6c58199820142c0f8ee94840deeef2f115cf50da625f7368c24af6fe74410f890f846a00000000000000000000000000000000000000000000000000000000000000000e4e381f9a000010000b400b400400003cc00000000000000010cc17165840dc00bc8f7da9df846a00000000000000000000000000000000000000000000000000000000000000001e4e381f9a0000000000000000000000000000000000470bc385a96c70693907dad2e98d013f842a00000000000000000000000000000000000000000000000000000000000000004a054cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8c0c0c0ee94842138c437649bf07959efb846402a8cd7c9e8a2c0c0d4d381fa900000000000000000000ed373f38d6d18c0c0f86294845adb2c711129d4f3966735ed98a9f09fc4ce57f847f845a05e7790995149cd394adfae18934f7312b3f381581362ed0c970a982e2f17869ce3e219a0000000000000000000000000d86e1929ca7611f284d9922ca44151c62c485701c0c0c0c0ee948492d79e09413a726bcbec69bf84dd32e4816fddc0c0d4d381959000000000000000000029911687df4000c0c0f09484d76430449a30e20805a52086f711912824340dc0c0d3d24990000000000000000000033a39287f55ecc3c2490bc0f29484f4014c74aa98615b608caa44facf6d03369983c0c0d4d381869000000000000000000000000000000000c4c3818624c0da94851b73c4bfd5275d47fff082f9e8b4997dccb253c0c0c0c0c0f8a994858b4d5df158fdf4b6dcd4b080841b27030178d5f88ef845a0b75cd79c04101231881d96727a22eb83fa5afbbe12b4b417017e2e6210285100e3e238a00000000000000000000000000000000000000000000004f36d35e872098209f7f845a0f9955cf8e6ac8d96ef7f9e8f22b5a8c20accfd3ca6306343edf359ecaa81b37ae3e238a000000000000000000000000000000000000000000008471ea7d2fd6d94c79d1ec0c0c0c0f83b9485b2b559bc2d21104c4defdd6efca8a20343361dc0e1a00000000000000000000000000000000000000000000000000000000000000006c0c0c0f694862f39ed6ea00f778b71ff2a890cd4375f62011ac0c0d5d482012e90000000000000000082d0de1f8785fd90c7c682012e823c41c0f8a99486e9bd5e42a9afde8d9c2594e84e49cc7718f381f88ef845a00000000000000000000000000000000000000000000000000000000000000001e3e207a000000000000000000000000000240236e1adc62a35811064fd8e09743ada0765f845a00000000000000000000000000000000000000000000000000000000000000003e3e207a0000000000000000000000001688a1197000000000000000000822abf050bfebec0c0c0c0f9016e94870ac11d48b15db9a138cf899d20f13f79ba00bcf849f847a0aa1064c618e4cfbba3acd5c01d73df21d593b4d11be600e22373110b1f5f7bece5e482010da00000000000000000000000000000000000000000000000000000000102f44180f90108a011e0f8d0aadf9f352219ddf351f4f9bf8a5767c1343a3539753694408353ef98a0354310daf1ecbc3b5ebca8f24c698ffda90e7edd6ad44ee1b60bfbc5b7fc47e2a09c54ea0b0b4353c90fbf64dd1e3ab5ceccb231a1a7ae6f365bb25cc5078ef67aa0bd476473e43e6965e788ce400469cf7e914dbb45a75364d50674af406ce2d9cda0d124ea604de008f89ecd12b262af761b16737e43a1e4a9abefc3d36362ea9c77a0d4383996bd54bf5a1434192efce65841e94d73322a64458bf3a7a9f6ab57c409a0db1449447e38bb5fc343657c1f8b68100f5e721fa06c8d255cbca0cd71399d36a0ebc9181e74a8f3a8001ad38ca6825ce7098d15c51cdd87de66c832eefccc09b4c0c0c0ee948750701562b7f13be081614a2b875a1a0277c175c0c0d4d381d090000000000000000000599b963b13e2e8c0c0f5948758b4449661306403ef25a5bf359a6dc2782dc7c0c0d5d4820140900000000000000000005e984f016f4b40c6c582014081e6c0f903f89487870bca3f3fd6335c3f4ce8392d69350b4fa4e2f90188f869a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf21f846e208a00000000000220ac8e21ab1ed6a5e99500000000003ad76b37dadf7bc76336513e230a00000000000221173d529d37d3b3029980000000003ad76b37dadf7bc76336513f869a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf22f846e208a000000000002b3fb582a82486cd602e640000000003d32fca4546a285774eaad0e230a000000000002b43f199e039d40e0631be0000000003d32fca4546a285774eaad0f845a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf23e3e208a0000000000000000000000300688a119700000000000000000000000000000000f869a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf28f846e208a000000000000000000001cc5a45af643b00000000000000000000002173f3f33ae230a000000000000000000001cb1ff36a283b00000000000000000000002173f3f33af90252a02175d0d8ef3ab7028ae5221371e123aa082ff9b633d969fafaec5a779f30127ba02a98f2679411c97049eec169ee6f16b3ea7321e5ae243b8379a6618d8484e695a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca07635c6f6fb0dc990d132e97ffe82e07606fac72c3d39da71ac41d6a8564adddaa07635c6f6fb0dc990d132e97ffe82e07606fac72c3d39da71ac41d6a8564adddca0b587e101db980eb9a3d4491a64340bd6e10aa0a7bfd3cc48f4b5cadccf068deda0b587e101db980eb9a3d4491a64340bd6e10aa0a7bfd3cc48f4b5cadccf068deea0b587e101db980eb9a3d4491a64340bd6e10aa0a7bfd3cc48f4b5cadccf068df0a0b587e101db980eb9a3d4491a64340bd6e10aa0a7bfd3cc48f4b5cadccf068df1a0b836b893bd676d88c16cc18e2cd838e74dba8b7bb45a7ea5304999612dd7fd93a0bc2f57311c21670184a5dbcdfc5939827a8f57c97f69166be8694e2ce000cebca0ca6decca4edae0c692b2b0c41376a54b812edb060282d36e07a7060ccb58244da0ca6decca4edae0c692b2b0c41376a54b812edb060282d36e07a7060ccb58244fa0cd902be0fc56a89d07a716bd9b7c456d37dc7915414959f0a71c648aabdc28e3a0e87579d97cad2f208f65e0e8103dc581de8b3fa3d6afc16ee30039101baf1cc4a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf20a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf24a0ed960c71bd5fa1333658850f076b35ec5565086b606556c3dd36a916b43ddf26c0c0c0f49488d0e9fd5058114be1df181271fc979beb66dcecc0c0d4d381d19000000000000000000004c17e825ff42ac6c581d18206a8c0f901549488e6a0c2ddd26feeb64f039a2c41296fcb3f5640f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e223a000010002d302d301c102f5ad0000000000003f9931acec966d86bca2361025f5f845a00000000000000000000000000000000000000000000000000000000000000002e3e223a00000000000000000000000000000146183e4b360cf103a7448aaf7059453c3e7f845a000000000000000000000000000000000000000000000000000000000000001c9e3e223a0010000000000000001f2add8a5728d45d38ae18fde00182967820493688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000001c8a0020c72644b6d1ac052ff0fc75ce872138c9c3684978ace385f3ce79ec651e607c0c0c0ef948919c0172774d21e2761406a733ab41a031e29e8c0c0d5d48201129000000000000000000001ab07777a3a8ec0c0f83b948958b1c39269167527821f8c276ef7504883f2fac0e1a08464d39f2e013f742832d01b64507041c699fe53e2b530f6afd3f7345d1a56ffc0c0c0f8509489e51fa8ca5d66cd220baed62ed01e8951aa7c40c0c0e8d233900000000000000000be3e5847d80e4ef6d482013e900000000000000000be3d8ce35ff929f8cec5338330bb01c782013e8330bb02c0f6948a14ce0fecbefdcc612f340be3324655718ce1c1c0c0d5d482013c9000000000000000001fe44384aeb1816fc7c682013c826957c0ed948b4ddd7a804978508c20b1238bde91e1f6f4802fc0c0d3d2729000000000000000002eeee5b0950e77eac0c0f0948b9b00961b8c2f885389918cdbcae213760c367ec0c0d3d2739000000000000000000006df99ee8e55cbc3c2731cc0f2948b9d99cc6997973a450aa5269bb2cd3672b283f6c0c0d4d381b49000000000000000000060addf9d3869f4c4c381b40ec0f2948beb76a4ade7bd4471319845bdd1db6f11013784c0c0d4d38190900000000000000000000fdefa2a0ca445c4c3819002c0f2948d00a749c1e2a66f08b35dcbb8bfb0ab79876123c0c0d4d381de900000000000000000000f4eaa061ab018c4c381de02c0f7948d18d00074dd99de3e3f22210b705b9b0a19835ac0c0d5d4820128900000000000000000d86c246129fa731fc8c782012883011477c0f2948d95149160bae314d8a757c827ed62177c335794c0c0d4d381be9000000000000000000002348bb15b3389c4c381be08c0f2948dbee8f3917049bc30fef01924a7ac79d16cf2b9c0c0d3d21790000000000000000078ebf7dde4b64baec5c41782651dc0f0948dc0567448f21f13965a06e48fdca122f0dae90fc0c0d3d236900000000000000000009803f4382f0498c3c23630c0f1948dfd856af8b868bdc73b4eddbf34511310402c03c0c0d3d2669000000000000000000611035e47f58bedc4c36681b3c0f2948e109b88c7765c3e7502781a5e3f034863df9e97c0c0d4d381c09000000000000000000024a2d9d78e8636c4c381c01bc0da948f4e8439b970363648421c692dd897fb9c0bd1d9c0c0c0c0c0f85d948fffffd4afb6115b954bd326cbe7b4ba576818f6c0f842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005c0c0c0f8ca949008d19f58aabd9ed0d60971565aa8510560ab41f88ef845a0d2a44d6468e40395efc88cb72a54683220fb6bd7f3b8b36c4e7b8a4e72dc8fd8e3e207a000000000000000000000000000000000000000000000003635c9adc5dea00000f845a0e175000ea091183a65fa6ddff418f171c94a1dd9bc4eeca4024d215ba1285159e3e208a0000000000000000000000000000000000000000000004008c038fbe087c01c7ee1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0f294903efd0817e41edef20e24876cfd24b463db4d9cc0c0d4d381ea9000000000000000000000000000000000c4c381ea01c0f29490582a67d18b707ec0e184c3b5fa97c9366e2a5ac0c0d4d381dc9000000000000000000000c8ccd22ebe7ac4c381dc02c0f2949119cc7792dd60e0dfbf6406ae3f6dc2adaeddf1c0c0d4d381ab9000000000000000000041d207ad8d2f69c4c381ab04c0f85d94914d5cb27cb30e80bde8215ff577ed63eb986b79c0f842a05bb716c9a951003d5f36b17b3484e884f28eac0491351d3f9d2d7dcff64fe1bda05bb716c9a951003d5f36b17b3484e884f28eac0491351d3f9d2d7dcff64fe1bec0c0c0f0949198cbdfca77d43053d691651c93c9106dad9176c0c0d3d234900000000000000000018d9488af461a24c3c23439c0f09491c87d02e2d38c440f11c4d9898d15ab8b5d97a1c0c0d3d21d90000000000000000002d4f777c6531d3bc3c21d16c0f39492b6b4bccb093d917c7934c621d3c34b7a240ae3c0c0d4d381879000000000000000000000000000000000c5c4818781b0c0f8ab9494314a14df63779c99c0764a30e0cd22fa78fc0ef890f846a04f101767299382d1c021d3cb6e9f8c5a041bdd4e7b82c383efc77b84e54cd198e4e38182a000000000000000000000000000000000000000000000001f1d929a4c6c01c1b2f846a0a873c99d3fefd4d3f899789b76a5d82cc5f4c93b381f4b83732b34cebb721f85e4e38182a0000000000000000000000000000000000000000000000016eddf90569829dc00c0c0c0c0f90a5d949503a25e9be822d6ab0f6cf5401c1cb2c8cef7afc0c0c0c3c26e01f90a3ef90a3b6eb90a37608060405234801561000f575f5ffd5b506004361061004a575f3560e01c80630d9019e11461004e5780638467be0d1461006c5780639642ddaf14610088578063cc1f2afa146100a6575b5f5ffd5b6100566100c4565b60405161006391906104b8565b60405180910390f35b61008660048036038101906100819190610508565b6100e8565b005b6100906102cc565b60405161009d91906104b8565b60405180910390f35b6100ae610448565b6040516100bb91906104b8565b60405180910390f35b7f0000000000000000000000003aab9b3145df48a22ae337d50d1d66a3b2a3bda081565b5f811161012a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101219061058d565b60405180910390fd5b5f7f00000000000000000000000026d85a13212433fe6a8381969c2b0db390a0b0ae73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610194573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101b891906105bf565b90505f5f90505b828110156102c7575f600182846101d69190610617565b6101e09190610617565b90505f3343846040516020016101f8939291906106af565b604051602081830303815290604052805190602001209050807f00000000000000000000000026d85a13212433fe6a8381969c2b0db390a0b0ae7f0000000000000000000000003aab9b3145df48a22ae337d50d1d66a3b2a3bda0846040516102609061046c565b61026c939291906106fa565b8190604051809103905ff5905080158015610289573d5f5f3e3d5ffd5b5050827fbe084de9f7dee36e14cada510169063ee31eab57fcf89573237491b9730ecd8f60405160405180910390a2505080806001019150506101bf565b505050565b5f5f60017f00000000000000000000000026d85a13212433fe6a8381969c2b0db390a0b0ae73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610339573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061035d91906105bf565b6103679190610617565b90505f334360405160200161037d929190610783565b6040516020818303038152906040528051906020012090505f817f00000000000000000000000026d85a13212433fe6a8381969c2b0db390a0b0ae7f0000000000000000000000003aab9b3145df48a22ae337d50d1d66a3b2a3bda0856040516103e69061046c565b6103f2939291906106fa565b8190604051809103905ff590508015801561040f573d5f5f3e3d5ffd5b5090508093505f7fbe084de9f7dee36e14cada510169063ee31eab57fcf89573237491b9730ecd8f60405160405180910390a250505090565b7f00000000000000000000000026d85a13212433fe6a8381969c2b0db390a0b0ae81565b610248806107ba83390190565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6104a282610479565b9050919050565b6104b281610498565b82525050565b5f6020820190506104cb5f8301846104a9565b92915050565b5f5ffd5b5f819050919050565b6104e7816104d5565b81146104f1575f5ffd5b50565b5f81359050610502816104de565b92915050565b5f6020828403121561051d5761051c6104d1565b5b5f61052a848285016104f4565b91505092915050565b5f82825260208201905092915050565b7f436f756e74206d7573742062652067726561746572207468616e2030000000005f82015250565b5f610577601c83610533565b915061058282610543565b602082019050919050565b5f6020820190508181035f8301526105a48161056b565b9050919050565b5f815190506105b9816104de565b92915050565b5f602082840312156105d4576105d36104d1565b5b5f6105e1848285016105ab565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610621826104d5565b915061062c836104d5565b9250828201905080821115610644576106436105ea565b5b92915050565b5f8160601b9050919050565b5f6106608261064a565b9050919050565b5f61067182610656565b9050919050565b61068961068482610498565b610667565b82525050565b5f819050919050565b6106a96106a4826104d5565b61068f565b82525050565b5f6106ba8286610678565b6014820191506106ca8285610698565b6020820191506106da8284610698565b602082019150819050949350505050565b6106f4816104d5565b82525050565b5f60608201905061070d5f8301866104a9565b61071a60208301856104a9565b61072760408301846106eb565b949350505050565b5f81905092915050565b7f74657374000000000000000000000000000000000000000000000000000000005f82015250565b5f61076d60048361072f565b915061077882610739565b600482019050919050565b5f61078e8285610678565b60148201915061079e8284610698565b6020820191506107ad82610761565b9150819050939250505056fe608060405234801561000f575f5ffd5b50604051610248380380610248833981810160405281019061003191906101a4565b5f8390508073ffffffffffffffffffffffffffffffffffffffff16631249c58b6040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561007a575f5ffd5b505af115801561008c573d5f5f3e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff166323b872dd3085856040518463ffffffff1660e01b81526004016100cd93929190610212565b5f604051808303815f87803b1580156100e4575f5ffd5b505af11580156100f6573d5f5f3e3d5ffd5b505050503273ffffffffffffffffffffffffffffffffffffffff16ff5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61014082610117565b9050919050565b61015081610136565b811461015a575f5ffd5b50565b5f8151905061016b81610147565b92915050565b5f819050919050565b61018381610171565b811461018d575f5ffd5b50565b5f8151905061019e8161017a565b92915050565b5f5f5f606084860312156101bb576101ba610113565b5b5f6101c88682870161015d565b93505060206101d98682870161015d565b92505060406101ea86828701610190565b9150509250925092565b6101fd81610136565b82525050565b61020c81610171565b82525050565b5f6060820190506102255f8301866101f4565b61023260208301856101f4565b61023f6040830184610203565b94935050505056fea264697066735822122076ef473b0cfdf9e6d581bdeb8885c20099c7a8a3bb8b83dcb25312e2027dc07064736f6c634300081e0033f39495480d3f27658e73b2785d30beb0c847d78294c7c0c0d3d20890000000000000000124a4d48fd5caaa00c6c5088301b9adc0f9015a9495ad61b0a150d79219dcf64e1e6cc01f0b64c4cef9011df845a00f35726ae3bff83fcad6995b6195b9a193e30855073b62d25fc1fd35ef7df12ee3e272a000000000000000000000000000000000000000000006a5cfdc0bff9a38060d24f846a0a75c56f09d8a96b55ae79285e0fe580b0ec76014fa22878b1db9aafad18d72fee4e381b3a0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845a0cf7d6310d012a42a76bb7228a6cf07ba28907b0ab736158bda4c3607018527b3e3e272a000000000000000000000000000000000000000000008ff169f56a6aa38f1b570f845a0eeea55bf32804ce36029867ad77ef16d33e74d0d1361b89a2534a5c497f47caae3e272a00000000000000000000000000000000000000000926e2427f2810ecdf5375e68e1a0eeb55c3dd980302982531d70b1334b544705f58d48d8c8f429429486cd476d59c0c0c0f902589495ae252633e9ea03bdfe67874b349b41163464cef88ef845a08e882feac761dc10ca2a0ec95a04947da660d6ff1895536a2c2cdde330fdfe5fe3e207a000000000000000000000000000000000000000000000003635c9adc5dea00000f845a09851164991f620a3b2d19d9cbad2beb69def2e6a66b85b309cfcd70612fd8d27e3e207a0000000000000000000000000000000000000000000001423fe1609d86175aee1f901ada00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000011a00bdfc17ceca3651c0f5ba28bbd281f2ae1b13186b756b39c53be6b3eaabf715fa0213d086b2fb4056a25ca6c8e4d04b7d7a6520ec5b56775df68dbf722c19d40bea0618059526d7d1bb5608c8e3a0740d1f656fa8a764ecca600a8e0e3e0c313ce66a06b0c010fbb9ec388ddd2f1cbf436c72ca3f9707d1acbe4ffdd60d7adac0f26a2a0812dc1b11d9d9ef2e86ff8ef8e28edce2507c887ff5b798551117d154f8e0a54a08c2000c1017e62d1ecc66de504b7ea2ef33ea67838f32342be49c748b99672bba0cce0021b97ad796f1fd12e80abd48c106dc9f8e7cccdc84c8ffa0f36635252eba0e90c6be031f8a055c6f812e34adfc65c81aa64866330e1e1d825ad44c1d295f1c0c0c0f29495cbda20bf1885492252c1820cb17bfc050b739ec0c0d4d3818f9000000000000000000000000000000000c4c3818f02c0f9014f9495dbb3c7546f22bce375900abfdd64a4e5bd73d6f88ef845a00000000000000000000000000000000000000000000000000000000000000000e3e210a000010000330033001b00057e000000000000000112a7597eea0a9d1b7d468351f845a00000000000000000000000000000000000000000000000000000000000000002e3e210a000000000000000000000000000000000020062a65ceed16e9793da03e3f2c7a8f8a5a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000023a00000000000000000000000000000000000000000000000000000000000000024a054cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8c0c0c0ed9495f20c5dfab020a1ac6180b65b7e75e72a1008e7c0c0d3d21990000000000000000000044364c5bb0000c0c0f2949683495e5bbd30d741eddd3f972d7ae5188423b2c0c0d3d265900000000000000000412b3709642c91b1c5c465828faec0f850949696f59e4d72e237be84ffd425dcad154bf96976c0c0e8d381f39000000000000005edeb87ba97a500fa55d381f49000000000000005edeb8748f56925eabacec681f3837a139ac681f4837a139bc0ef9496c195f6643a3d797cb90cb6ba0ae2776d51b5f3c0c0d5d482011b90000000000000000178c3022cf7207236c0c0f85d949759a6ac90977b93b58547b4a71c78317f391a28c0f842a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002c0c0c0f094976650814731b98f7afefdb57b015f31205fd2bac0c0d3d26490000000000000000000014bb4c514b970c3c26402c0f6949766f924c835f49ab122b086bf59ae6416c292c7c0c0d5d482010a9000000000000000000144dfa330804804c7c682010a8201acc0f83b94985462c9aa4d6c3ad59ae6e1e9c0c11347ed1598c0e1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0f8ec9498c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5cf88ef845a00000000000000000000000000000000000000000000000000000000000000036e3e208a0000000000000000000000000000000000000000000000000000c98cc84d5f0ccf845a03b05d2bff73ff12707cb4ac03597c8d6176c69b849b8f2ebafececb921a3c116e3e208a00000000003ad76b37dadf7bc76336513000000000000000000003e1dd2784022f842a0000000000000000000000000000000000000000000000000000000000000003da0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f87e9498c3d3183c4b8a650614ad179a1a98be0a8d6b8ec0f863a038cb777087d30c725da4e6f250353cdec5e3824ff4666b967bf755b67c55b0a4a038cb777087d30c725da4e6f250353cdec5e3824ff4666b967bf755b67c55b0a5a085b379096c46275e23a78d31780a35a3347db19cf17aa35c8f6b6325dc2d31cec0c0c0f902b29498db502215da1ad9f626d4a0090a8a2f4971003cf901f0f8afa0d01a293d013d5426dd5cc2a98a194f6994ad72d277cc3a5942ea20b2a409b465f88ce25aa00000000000000000000000000000000000000000000000004e2e006c867ee400e25ba00000000000000000000000000000000000000000000000009c5c00d90cfdc800e25ca0000000000000000000000000000000000000000000000000ea8a0145937cac00e25da000000000000000000000000000000000000000000000000138b801b219fb9000f845a0d01a293d013d5426dd5cc2a98a194f6994ad72d277cc3a5942ea20b2a409b466e3e25aa000000000000000000000000000000000000000000000000000000000688a1197f8afa0e0fa9bdcea0c363399173621ce766be19c26afa0a2c80805b59df661ef0b696ef88ce25aa00000000000000000000000000000000000000000000000004e2e006c867ee400e25ba00000000000000000000000000000000000000000000000009c5c00d90cfdc800e25ca0000000000000000000000000000000000000000000000000ea8a0145937cac00e25da000000000000000000000000000000000000000000000000138b801b219fb9000f845a0e0fa9bdcea0c363399173621ce766be19c26afa0a2c80805b59df661ef0b696fe3e25aa000000000000000000000000000000000000000000000000000000000688a1197f8a5a03e0b7bd22739b28f4246949d0ae401774af42657d89995345148ada71a16daf0a0d01a293d013d5426dd5cc2a98a194f6994ad72d277cc3a5942ea20b2a409b467a0d01a293d013d5426dd5cc2a98a194f6994ad72d277cc3a5942ea20b2a409b468a0e0fa9bdcea0c363399173621ce766be19c26afa0a2c80805b59df661ef0b6970a0e0fa9bdcea0c363399173621ce766be19c26afa0a2c80805b59df661ef0b6971c0c0c0f85d9498f3c9e6e3face36baad05fe09d375ef1464288bc0f842a00000000000000000000000000000000000000000000000000000000000000000a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f83b94998c061820903de9af6d950eddb978af328c8326c0c0c0c5c482011f01dcdb82011f97ef0100411d38d27f6f2c7f3b70ff29dada64cbd7bfa9b2f29499d5edff0165df808eec380f3d1932325f55fd46c0c0d3d2759000000000000000002e83bd87bfccfa7fc5c47582013ac0f2949a0a11c3a1827baa2f8187658a5ebc6b611c2a53c0c0d4d381e79000000000000000000000000000000000c4c381e701c0ee949bc478678f97c82bc9be301464acf5c9c4ce8f01c0c0d4d38199900000000000000000000144429cd96067c0c0ef949c1a360c220bcd53c2aad612d17210f7aaf5ffc0c0c0d5d48201269000000000000000000001085e7e748740c0c0f863949ca8530ca349c966fe9ef903df17a75b8a778927f848f846a0f0ef4cdcc1cb5911848e4349c1b3c92225ab10baa3b6a42bcaf6d713c4b7ccc8e4e381cba0000000000000000000000000000000000000000000000562e369224bd4000000c0c0c0c0ef949d320f5c353b38064451b026210dcc0af42a00ccc0c0d5d482012b900000000000000000004216fb196a178ec0c0f87e949d39a5de30e57443bff2a8307a4256c8797a3497c0f863a00000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ec0c0c0f901a5949dfad1b7102d46b1b197b90095b5c4e9f5845bbaf90168f846a02d75c38ca86ceda630251d44cb2f708dea1a3b58cfb2649eb61bf2a3d131ee57e4e381bca0000000000000000000000000000000000000000000000029b004d1a9d3640000f846a03b0d4f93a460bf9a01f865b6a20338ca4f15cd68ff19d4134589bf608c7139bae4e381bca00000000000000000000000000000000000000000000000000000000000000000f846a04ae535cd77c06b26ce05139418f8abc4169f06c338bcd47f7680680a58ec5d9be4e381bca000000000000000000000000000000000000000000000003635c9adc5dea00000f846a08e01c616c341f0cf08a4dcc7360397f6a73ca88251123c5a9dfd08f8bafddfc8e4e381bca0000000000000000000000000000000000000000000000010f7bf61a7a9440000f846a0db305dca8f6e2c25995b0dbb821239c2a8ad96b78ab221b9d84687bcbcc60d3be4e381bca000000000000000000000000000000000000000000000016ebb3c097e6d940000e1a09d199992ad03b84d8843ae23ba7165e8448dcfe6ac421eef22f0eb1e6258752cc0c0c0f1949e19bc2a0170a58634dc4bbecf4b5df64dc1b912c0c0d3d27c9000000000000000000000000000000000c4c37c81a2c0f83b949ec6f08190dea04a54f8afc53db96134e5e3fdfbc0e1a0c6521c8ea4247e8beb499344e591b9401fb2807ff9997dd598fd9e56c73a264dc0c0c0ef949ff0a00cf9d3a7949df87ea58311bb1c5328d9c2c0c0d5d482012a90000000000000000000c56bed013ccc3ac0c0f8ca94a0b73e1ff0b80914ab6fe0444e65848c4c34450bf88ef845a08b25b2d179e99dc765952487fa4ea9aa95337a18ccd74edd852b419f7ea48c81e3e26da000000000000000000000000000000000000000000000000000000018a3ce9e40f845a08f060a7e8fbfb20e8b3f45562d6bedf7d45f0ad0ebd2a48934eaa558cfd6fd54e3e26da000000000000000000000000000000000000000000000000000000cd37a84dcfbe1a00000000000000000000000000000000000000000000000000000000000000004c0c0c0f91d3c94a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48f91a48f847a0000000000000000000000000000000000000000000000000000000000000000be5e482011ca00000000000000000000000000000000000000000000000000091e0c5640cbd67f845a00403a8ca3a9903ca93cf71f6768e67d9c7686c7c285e94db9a93d79fffb1e6cae3e26fa00000000000000000000000000000000000000000000000000000001301e28bc8f86ba007081a045c3dbf2e63b62a407ef205e7586e2629d2e2b95ff093308ca0ff3727f848e381b5a00000000000000000000000000000000000000000000000000003338fe2bbc60ce381baa00000000000000000000000000000000000000000000000000003338f65724d74f845a013b47f23d2a1108525541d5ea06db599b85218b4099197240b2ce242653286b9e3e208a0fffffffffffffffffffffffffffffffffffffffffffffffffffcd032ea9aebc5f845a016f44a28305a3de6619326825df2d5b9f237445dc76089ca6a36cbc218c84016e3e228a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffe8435db6bff845a01b0c0f0159019b48c57aa1c9a5c120b3f8b13f80cdd673344a5443e1525d4f57e3e22ca0fffffffffffffffffffffffffffffffffffffffffffffffffffff6f03d0aeefff846a01b91f984d257abdb09c24c60317087b94480f5b6f25cc1e79ea4d913f4c4d2b9e4e381a1a00000000000000000000000000000000000000000000000000000000000000000f845a01cfe18bf93cdf05690b0062c9de5d226d3a2c24e01f06d145cf8b3e2daf50237e3e229a0ffffffffffffffffffffffffffffffffffffffffffffffffffffcff2f9f5f71ef845a01f21a62c4538bacf2aabeca410f0fe63151869f172e03c0e00357ba26a341effe3e223a000000000000000000000000000000000000000000000000000003e6a2fc77b83f845a025336f8c6add5ee5dffdc2d268b04933e2b26aecf651507285be25d3f2120dc2e3e270a00000000000000000000000000000000000000000000000000000000000000000f847a028bdbd20214d5db5a524544a9daae0b25f2b7fa90ef4687010ee5b1212ba34e9e5e4820121a0000000000000000000000000000000000000000000000000000000025f0ac98df846a02aba44f4280c6c9754570434b3b2d1846bd3de45fdf03af4031deb742b0982e3e4e381b4a00000000000000000000000000000000000000000000000000000006152c7572af846a02b4f5bc1e37313a2e9c7f897fc88d97382bb24e64f7a038ea443cc53fe46b94be4e381a1a000000000000000000000000000000000000000000000000000000091b8f74851f847a02bed307e395012c0177f428c356a6332ecc58ce972eff3d36392e192ec2bfbc6e5e482010ea00000000000000000000000000000000000000000000000000000000701e7a332f845a02c2e75085c617e01c003f77162c742100495af062d8ccbd8ad0f00c43cc7878ce3e229a000000000000000000000000000000000000000000000000000000000000359caf845a032b8c3e0163a3eceb0dd8f4a69ff6a8f786c1f0b2824e29def2a833031a4d374e3e207a0000000000000000000000000000000000000000000000000000000dfd1a01798f869a0368c3f5f03e5634b3e4381c9c3caac98f4d254c7027fd14b53436c90d060fef4f846e208a00000000000000000000000000000000000000000000000000001cc5bbf83ca9de230a00000000000000000000000000000000000000000000000000001cb216d3e8e9df847a0388062d5a9f733d43b8c03df09117064fe91d11c1b44351b834d8b3c3d0c8ac0e5e482013fa0000000000000000000000000000000000000000000000000000000065bec0c00f847a03b9552a383c613359f42e8efcad3d02a7d52b94c1f5a55810d70edf307f4e4a7e5e482010ea000000000000000000000000000000000000000000000000000000000396636c0f845a03d0c86eb34c0568c1be30a27ea167721c77bcfec1f1d2867daa03c9441f0c811e3e274a000000000000000000000000000000000000000000000000000000006a4dce33af846a0405c3165962566349997e7ade88f32fa21fe1e88f7929ccd59085e2bb237e1b3e4e381d7a0000000000000000000000000000000000000000000000000000000001dcd6500f847a043cb05957f9185d0b7cbe5f806b641971b2b7b2225fca1f7edfcc6d8ae5c5b59e5e4820142a0fffffffffffffffffffffffffffffffffffffffffffffffffffc3a2a6d16b2eaf846a0446604e397083529c53ad480b1c55633cac95c735b2eda7f254bd3b3030e83cbe4e381d5a000000000000000000000000000000000000000000000000000000000f42cbaacf847a048cd8e013feae6dd6853385c1d31fe4df90230e5ec726300d9f5e34b7b15e13ce5e482011ca0000000000000000000000000000000000000000000000000000000000010bc05f846a049485e14c2b5c1d2611f08a6dcbd51a2c4953444e6d78fbb7f0ce6ce635ea868e4e38189a00000000000000000000000000000000000000000000000000000000000000000f845a04dbb2215e3ad761c55be3ba0e131fa99e8c058060b10bc090c66aefb0b7791dde3e208a0000000000000000000000000000000000000000000000000000025fe9a9330f4f845a04e4c0dac904fd59242813530a7881d473265c091b3208a3775628deb31615018e3e22ea0fffffffffffffffffffffffffffffffffffffffffffffffffffffff8e1ef549ff846a04f9d5bbda28e579b0d753b7ca4b799524362c18ae4f3d8686597de0649319b74e4e381baa00000000000000000000000000000000000000000000000000000000144c10238f845a0573dc4942e986ed220476310bfc2963e22d785708d7092254e761a8870a434b7e3e210a00000000000000000000000000000000000000000000000000000001c3af93a28f845a0579daf8760b73f773247e46549d90262f85d3f88e0791ea09dc90982358fcc5ee3e227a00000000000000000000000000000000000000000000000000000000000014aa6f847a057d18af793d7300c4ba46d192ec7aa095070dde6c52c687c6d0d92fb8532b305e5e4820120a000000000000000000000000000000000000000000000000000006658bc106dfaf847a05918522d81a707d247543fa58a114c8631bbc4b976e099438ead7e1df9c55182e5e4820142a000000000000000000000000000000000000000000000000000000036cd903e30f845a059e52fc8e98ff9c9372e71ea7a66e3a0aa36468dc746b4160ab339c47e931566e3e208a000000000000000000000000000000000000000000000000000000014d1af62b5f845a062ab965db00f05dbcc7d35fd84c307563fc8904ac2d05ec4912622d5c269c16ce3e22ba00000000000000000000000000000000000000000000000000000000000000000f846a06377367ae5d683c63df1fd56270a0926d0a7467373c17b9a3773ff054f1971c0e4e381b5a00000000000000000000000000000000000000000000000000000000005defda0f845a06849d239d5844775b16c52af3b2b9616758170cbbfbfd2131d698ddea030ad48e3e26fa00000000000000000000000000000000000000000000000000000001292909ceaf845a069ea9dcf41ae69de972396868946ce09924d9df2df7677ae366da949f2678de2e3e270a00000000000000000000000000000000000000000000000000000000442f832dbf845a06a7b5495a5f5e82a088149d7fd806b94bc72cb8b3b6eb11f4bd1c3c086526396e3e207a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffede07d9b05f846a06a9ce6256c4c8fe5291cb1967fd41fd30d5c662227f36fbdd306bff2e8435efbe4e381b4a00000000000000000000000000000000000000000000000000000002c3ce1ec00f845a06bc64cb6c48d697e2319078d295b7393e104818737ad694bea9b928cdcbaabd5e3e261a0000000000000000000000000000000000000000000000000000000000632ea00f846a0703224e8552824c017c9a6a7af77dc93ae940ab9a40480c64f63d12f2214b3bce4e381a4a00000000000000000000000000000000000000000000000000000001a842ac8e7f847a07599c49598f9115df88ff41af1ae9d8c38b52091cf63f46c737a6634939cbd7ce5e482013fa000000000000000000000000000000000000000000000000000000073f0718965f845a07d0ee6848e9c0a6e4d77f6a7041dbfaf7208e7d7650c46bafd6a635d1ae04ea6e3e220a00000000000000000000000000000000000000000000000000000000001c6906bf845a07f155bee5979bf810991e861eafb8119cf00cc265482cfd4b004eb742dc393fee3e261a0000000000000000000000000000000000000000000000000000000000637cc45f9013ca082fc8338773fc4426092c36a88f1631578741710523a800748804783a20f57a2f90118e227a0000000000000000000000000000000000000000000000000000089e54e9ee54de228a0000000000000000000000000000000000000000000000000000089efd9c1e64de229a0000000000000000000000000000000000000000000000000000089fe0893090de22aa0000000000000000000000000000000000000000000000000000089fe0c8dea56e22ba0000000000000000000000000000000000000000000000000000089fe79158b2de22ca000000000000000000000000000000000000000000000000000008ae74dba9b2de22da000000000000000000000000000000000000000000000000000008ae753b07c2de22ea000000000000000000000000000000000000000000000000000008ae7746e4a5df88da088483bf000a1009d5df96db9349a9e2c3b91af50be04719cbc643daad6e36e36f86ae264a00000000000000000000000000000000000000000000000000000005d551fbcfae26aa00000000000000000000000000000000000000000000000000000005f4e806aaae38189a00000000000000000000000000000000000000000000000000000009287863446f845a08bfa486c82ae4ad4f79ac4b5235f23d15408d726e237b266f693aa4dd66bf9dde3e220a0000000000000000000000000000000000000000000000000000078f926369f8ff845a08ec66e8cc73ea976ba36e242b81bb6837eea609cb224831f21e15e16cd61ac84e3e22da0ffffffffffffffffffffffffffffffffffffffffffffffffffffff2ff7240df9f845a0912c5c1e6f999b60f96feb06dcc847403c149facada9563b85e42f1e4c74e44ae3e225a0000000000000000000000000000000000000000000000000000000001e8b6fd2f845a09254cb65314db3d2d7ca17f753f1d9c7f1b6fa05111d18d10ed5b9519d1b247ce3e208a00000000000000000000000000000000000000000000000000006af725ba583dbf845a09480b08a056d73ea26cb86f728aee215dc077e9979e7a8a973e055f2bbe76d66e3e202a0000000000000000000000000000000000000000000000000000000190827c786f845a095a8b87fafd40fb07c9ba218831195b85a3b0341a782d6aaf81c2889fa577cd9e3e227a0ffffffffffffffffffffffffffffffffffffffffffffffffffffe5b83d2cd4fdf845a0985e01148b0463093183b86a09c5b603149b0ae0b41e72d8e62756e4665d1e62e3e210a00000000000000000000000000000000000000000000000000000001741e53810f846a099be877cdaeaa967ecefd0a4dc1a8f04df06b7b6f1fe33a208faf3606a8dcc10e4e381a6a0000000000000000000000000000000000000000000000000000000007df4e907f846a09c7c5d9cd3606cac3a7f2ea7dd3b916bfadaf43a5ab2fbe69d11fc36dc112d08e4e381a6a00000000000000000000000000000000000000000000000000000000029f28680f845a09e1447541c5b5d2520d0edad47ed9d2498027eacee92438c675d79875388b9f1e3e207a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffa7bf093012f845a0a55e3ee1a2cf8165d514ca1a10b7023d3f578fd115a81ad42cedd4ea26a94bfee3e251a000000000000000000000000000000000000000000000000000000010d75f2e6bf845a0a6ad7d9647a4af31649116ec7f8ae68fe377968100f32dc76d4023b11b67d8ebe3e228a000000000000000000000000000000000000000000000000000000000000003e8f845a0acec6a9dc14aa6f92f59567b8f97d09140452ddae6f906dbf533b40ac75d02eee3e264a00000000000000000000000000000000000000000000000000000000000000000f845a0aea84ce797a9726e2eb216af35aff86089a97682167e4727f8eaf73f6c05ada4e3e26aa00000000000000000000000000000000000000000000000000000000000000000f845a0aeafead97bed83438c2afcf7a89b579bc8a372420c97e4e21414f62d9b8efb3de3e225a00000000000000000000000000000000000000000000000000000000000000000f845a0b5340454f0e9164591d809f517e96a2c8c3dc6f928aa8d3e26ed909435286f53e3e274a0ffffffffffffffffffffffffffffffffffffffffffffffffffffe1f46b23e8fcf845a0b8201eb34c3d7359dd6941d52f94f4c2068619b673d1fb8dc9e43aab9295c673e3e22ea00000000000000000000000000000000000000000000000000000000000000000f845a0b9f6591fc52eba3e15c8a35522a85e6d320e9084debe30399ae4770e63f89389e3e224a00000000000000000000000000000000000000000000000000000040933dd10abf847a0bb77b8825753962eb316185bc7456a8e44c6191463d4e3d1fe1a07646764a3f7e5e4820121a0000000000000000000000000000000000000000000000000000000000ec82e00f845a0be18d092cdba38c47e9610abe635ec24394c9e691d55f0861c9255ed64ddd2bfe3e230a00000000000000000000000000000000000000000000000000000013a52453c00f845a0bfb47d0454d8de9b5c2a21355845088b948e43ac0cf66ab853b4d92ab163bc5ae3e22da00000000000000000000000000000000000000000000000000000000000000000f845a0c5dd1caf4fede68327b6fb815283538b7291ab366354d84166fdd110d5b597f3e3e274a000000000000000000000000000000000000000000000000000000000b6f8cb70f869a0cc236083e86ee3df0f3160002f381f1404bd44c4dec1322196f34d52548202f5f846e207a0000000000000000000000000000000000000000000000000000000001d78357de208a0000000000000000000000000000000000000000000000000000000001d78357af847a0cd1718b39694c8b71b5b7030a9f4416a979d5bffcde56263723356d0f75e4776e5e4820142a0000000000000000000000000000000000000000000000000000001699c721b2cf845a0cdb7651d4e5401de141afb114df033f0f4f3fdf0bae5bbf312c8baace8a83c9ce3e22aa0fffffffffffffffffffffffffffffffffffffffffffffffffffffffff2b0e68ef847a0d24a61e94450386b8c40fa9a314eda432022998ab4730a824532e129094486bce5e482011ca0000000000000000000000000000000000000000000000000000168f0346040fff847a0d3c7bccbcb9a38b179e4ff6c239fc7b692bc0144074f5c8371ef03ace723161fe5e4820142a0fffffffffffffffffffffffffffffffffffffffffffffffffffd9bc5b8ee0e2cf845a0d6cb24c70e488b7b7eefc44fce798fabbf91f0ceeb167552e13e71816a239969e3e22ca00000000000000000000000000000000000000000000000000000000000000000f847a0d7c2ca1b2001ebdfb058bef6a6231a3c83cff193a748eecbefc3e36fdd8bc0d6e5e4820125a0000000000000000000000000000000000000000000000000000000d3595eb4d2f845a0d98c4f2afefeb3cf39eaf17e664c256211e9c21f9f4996ad98a8b1f55237590fe3e22ba0fffffffffffffffffffffffffffffffffffffffffffffffffffffffd1c9b4469f845a0dd21eaa2a7b91d700e186a088ec24a6ffae451cbb867f014da63e9784f6a5049e3e210a000000000000000000000000000000000000000000000000000000000005e7519f845a0df6b2ab211358052d6a681774c49d8a6e0bfaf0fdf575141826340ff9dbb5823e3e251a0000000000000000000000000000000000000000000000000000008f50f675d0af845a0e455a23b9bd8cf2e61da796ba2fcd25d4308f14e1467e151b5f748657ffc8c3ce3e22aa00000000000000000000000000000000000000000000000000000000000000000f845a0e5b3177dfa02823e57fc5c907cd9f3aa3ca557747f255abfad1faa15be6adc16e3e202a0000000000000000000000000000000000000000000000000000000c9c55130daf845a0e9a042b510c4a11346b2bb8943779a6be59156b3193c52e4e1d3b52c400ec46fe3e210a00000000000000000000000000000000000000000000000000000019e686509d8f847a0eb7cc7a75a8aa77a2cf7ab48f7be4a9a78f7adb81d3a7e395f3dcef71cda1bc9e5e4820125a00000000000000000000000000000000000000000000000000000000000000000f847a0ee6f9ab52d4876837d498b3cbcfe9ffbe6fb057563bd0fe5d3db71854ac6219ee5e4820120a00000000000000000000000000000000000000000000000000000036485e3e8a9f86ba0f44681571ddec7a61b19173091fe5695116de851b72e26704b9c0765af0929e7f848e381d5a00000000000000000000000000000000000000000000000000000a574331ad957e381d7a00000000000000000000000000000000000000000000000000000a574154d7457f869a0f56408d23e6790fec5453738cf042a4a3ef7ec36e9ceae8978e4ffce8e903bc3f846e223a000000000000000000000000000000000000000000000000000000b10024def30e224a000000000000000000000000000000000000000000000000000000b1258208d5ff846a0f99328222adaeb11584f5a4c4ebb1641966b73a9a615ca1590569555a0eaffffe4e381a4a00000000000000000000000000000000000000000000000000000000001036640f845a0f9d167372ad3ed68278b8207f5ac8f4771f1fd52f662e287847e8f1b743684aee3e207a0000000000000000000000000000000000000000000000000000000000b7955aef902d6a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000006a00dd5d254edfad4a1e855ecc27eb0259904a06513d6c6c00150e3838c28855f61a010d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390ba0115839bfee55578761c7edec3b2ff273385ed4d5df39b75ab78870ff966c69b8a01f1a80db9a82d9fd4ea3322b7d2dcad52fb678229d487c4bb44e821a2544709fa02209089ff8bd3b7bac6ff21ec014fbd229af53b4ecc03dcfbd102128759cb6c8a024bd6c6f7d1eeabad95a250d5ab0bbeaae9c7223e36ba7b50aea221573ce7122a0260b0b4579836006fdc9488505d30b092b206f5c35a9e8a198bfc75f36f18ce0a02ea87998dc80e6571fcfa09535ee2fe763d2a4d8fe160db740860905088012f2a03c2304a4bd8ffa93cccd3b8e4c28dbcd5c94236b1a7b40e3dc80ba6953b4cc7aa046f92c9c87fd0acc2c0b8d6eafa5c91e008e40b8eb918f129e106a10acdd5f00a048cded43b819614416e7748119a4c9311836b0c62ef8044c4aa24e9ac91548ada05b60a69638bcb2dd73cec9b8e9eea3257269241aa3171fc39b72bec82367040ea0659f5b53123b3e7a886575e106645d4d7c5af120440f3f409542b3987fa1ea07a07050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3a0aacfabd8359c7d556c0806d682fd47987dffc8442f69a0ee2211e786298eda53a0acfa85f8dca23f171b19bf4838ca8f35894504326d0e8354507c4fd5f1a60701a0ad83b682faf556b6f35037f886f1baa3824496554eaf6a410686373783f1ac14a0c1e9f6c54e8b284fca05f4d31686794a1ceeb011fe91b9e4cd2e9d58809afc01a0d4833240551f51c64948d4971df43f5379482d5ab5c61bc3e254bd13d086fde3a0d4c2888f964e4f1e878a271ae0188061e68cb48175ccfe7fe23880b7bc1d9b2cc0c0c0f9015494a1444ac5b8ac4f20f748558fe4e848087f528e00f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20ea0688a11970000000000015aad205dc2d47ed100000000703da631a866375521fbf845a00000000000000000000000000000000000000000000000000000000000000009e3e20ea0000000000000000000000000000000003f7a32ae251375185490ddbb357061f3f845a0000000000000000000000000000000000000000000000000000000000000000ae3e20ea00000000000000000000000000bfde57035f1363b6bfd440f452ac631ad88b8f2f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f094a166d72fa029fe68b2e9d8364f9ca88a7814dbb3c0c0d3d24190000000000000000000027285d998e881c3c24101c0f094a193c160aa1a8971eca9490cafd807250791b7c6c0c0d3d248900000000000000000000819e93162c081c3c24805c0ee94a239366d40b724dd2b7f1e5750e82cf4d56aa7c2c0c0d4d3819090000000000000000000930512cabb3c66c0c0f88f94a26148ae51fa8e787df319c04137602cc018b521c0c0f874d2799000000000000000012de2164dc8a58509d27a9000000000000000012e66bfd802b4a1d7d27b9000000000000000012eda5e32b6155e17d27c9000000000000000012f5c1d5bb06eba57d381869000000000000000013033e1a3a82d8fd7d3818790000000000000000131593f491c095557c0c0ee94a2b7d79ef29b966e4ed54f4137177aaff9d4ce89c0c0d4d381eb9000000000000000003a801bf7422e8000c0c0f87e94a34f5e6d1a5c89ff19a8bbc6c7d9314d84954366c0f863a00000000000000000000000000000000000000000000000000000000000000005a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f394a377aa6822603cc52e4e9ed6eaa045c46ef6b3e2c0c0d3d23590000000000000000031a5045898b304c8c6c535830a52e2c0f8e794a3b5efa6b7004cae689ed1019e106a84df9e5060f847f845a00000000000000000000000000000000000000000000000000000000000000005e3e205a0000000000000000000000000000000000000000000000000000000000000017af884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004a04a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8a088c285dbebbd05a382ad5ab67cf5ffc5a2a46a2cb7c6e3e2ada0bb6a2bb716b5c0c0c0f9019194a3ee21c306a700e682abcdfe9baa6a08f3820419f88ef845a0622ae4d47e6dceb758d5c31172f2ca5950fcd50f7be12b1b577122e5a84f24a6e3e226a0000000000000000000000000000000000000000000008fb013022155f7d69158f845a0720891d7585a0424ee35660cc9ec9a28cb4e4cee1d8e426c15e60acd7f3a19d0e3e226a00000000000000000000000000000000000000000000000af993d819067f5341bf8e7a00000000000000000000000000000000000000000000000000000000000000001a0391b3b40ccfaff5a5492b970966cd01a33076e765e2920215207137fb9a83dcba052f9bb52ea281646e34e82a2e2ec54444b06ddfb8169009272b333f1d9d56fb3a056f79616b2ea27290683fac649a963eee217708f1d3318adbc620456ff8bea0ca0710f540c7b4ebf1305bd038276951ca0b923ab9c5b3c14cc6bdb4633e7a770f3a0adfa909a1451f37a80c1512e4a327b7901ff3332d9cb192c088587196a476d34a0f9579fb00abd61abbaea6ff3d70388309db96eed599a97c0a651686c6d74fe2fc0c0c0f9026394a4c567c662349bec3d0fb94c4e7f85ba95e208e4f9011cf845a00000000000000000000000000000000000000000000000000000000000000004e3e265a00000000000000000000000000000000000000000000000b024da72b4e4433708f845a00000000000000000000000000000000000000000000000000000000000000005e3e265a000000000000000000000000000000000000000000000006f0fcd33a89fc8fd21f845a00000000000000000000000000000000000000000000000000000000000000019e3e265a000000000000000000de99fa882153f0500000000000000000de99e1a251361f2f845a0000000000000000000000000000000000000000000000000000000000000001be3e265a000000000000000000000000000000000000000000000000000000000688a1197f90129a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000ea0000000000000000000000000000000000000000000000000000000000000001ac0c0c0f494a55e4b9a4f637d7515fe83b4cce59b0af6a66c54c0c0d4d38184900000000000000000003a9e5feeff948ac6c581848201c7c0f9025d94a563ae4f348e93c7f50e8fcbffafa44f5aed0b36f8d5f845a004c5856110b85993c260ca59766f06b99c59f1f8e98415cbf2a9b2d5e6981fc8e3e20aa0000000000000000000000000000000000000000000000e5a60a0c6aac1d3e1f4f845a018e009cda939d1fd36ccab098bf96284f74d74cf8260d6551fb99d3bffca540be3e20aa000000000000000000000000000000000000000000000006d182c993c6f1954e1f845a08554e4187951285ebada776cc1d42cd454aa0e31d552f89f416720c43a8de42be3e20aa00000000000000000000000000000000000000000000118a6ddaa03dcc885696bf9016ba00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000015a0000000000000000000000000000000000000000000000000000000000000001ba0000000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000000020a00000000000000000000000000000000000000000000000000000000000000023a00000000000000000000000000000000000000000000000000000000000000025a00184490f07303c6c826dd60f64aa3223736d17e70f4516d1e19cff0d5dc9e085a04020d20232fc91a17a73dc98e19c39e5f97ab3d3d127dca225db5d4614aaaffaa0796349f928668a48ac84ca093ff677e016847f2a17dc74a939097965d0d0236ba0a466b5f32b5e0f2546af9e810dcd22074b2d1e86c766541c11833875c3ec7676c0c0c0f294a56f846233c2ac0d83fb5b1f9a64aa9cc1885b02c0c0d4d381fb900000000000000000000003c5ac56b808c4c381fb03c0f83b94a58e81fe9b61b5c3fe2afd33cf304c454abfc7cbc0e1a00000000000000000000000000000000000000000000000000000000000000002c0c0c0f394a5a13f62ce1113838e0d9b4559b8caf5f76463c0c0c0d3d21c90000000000000000054135dfa49087dc2c6c51c8305176bc0da94a5f565650890fba1824ee0f21ebbbf660a179934c0c0c0c0c0f9010194a69babef1ca67a37ffaf7a485dfff3382056e78cc0f884a00951aeed2e27b702ce516900c08a7c82271f51c487c2d80500b85e40af550d41a09ea658ca2d24493910b2374d3573a0281cefd09b43a9f7f583f0676365bb92dea0a7194a069909d28dfed8825ef6d2521aaf009fb3233a57fba5e2a5fd5ace3899a0afba4495c9b6be110d982b68170bc068f64a356bcfdf2ac9c368098cdc35064df861d21c90000000000000000a75d2b59646ec457fd22290000000000000000a75d2b5964712e797d22390000000000000000a75d2b596475e28afd22490000000000000000a75d2b596483ac4c7d482013c90000000000000000a75d2b59648e320dfc0c0f9022c94a7ca2c8673bcfa5a26d8ceec2887f2cc2b0db22ac0f90210a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000000015a00000000000000000000000000000000000000000000000000000000000000017a07d4f62d7a1d233431639862c23098947a59fa9d610342acf75daa657c10f6cfaa09144aee7f37b7f43b6b90b5f10849f7e24f0d10aec9ad49f57e3aa0eb644251fa0acabee26514bb791c4f3218b82e44269bbee7e9c7cb78209c6483d49ed0c8663a0aeb398aaef01950a3b20c728e1997bb95d411b0de44b9c8c5376cf18fee7cb8ba0b64dca7c2c7c1042e75fbf9b93ca1120ef9e6feac25da501460cd6d486bb8882a0d8f023ef571f4c023b56aad4efa6735cfb9b0d823767a0cec823d80982edadf5a0ed4a303e04cc9a91fc1acab40e65e7a14b8b8cc64c83ade8187695355ea4625ca0eed48e1b7f1147de10c3442cf7f36d7b541207e7f0041b1a7c8c303e3c978fb2a0f8d6e1552be1e8b38e47ebdfc2dc3d3a17782cf5647bb8549806e12f62f320fca0f8d8af571bcf8331a05936e065bfc9134b6461ab3226d0f94b32b989e63dadf0c0c0c0f494a7de6bcf69afd444bf3e229b72377cf0efec3980c0c0d5d482010d900000000000000000000633dd23055946c5c482010d50c0f094a869c2b2546945b17fbcac82127e4b50b7b46eecc0c0d3d24b90000000000000000000074516d2822081c3c24b03c0f494a8f7775002e4f7deed0702373dc7612a66be0313c0c0d5d48201329000000000000000000001656bbdb93fb8c5c482013202c0f394a9ac43f5b5e38155a288d1a01d2cbc4478e14573c0c0d3d2549000000000000006516fc5b41affba2151c6c5548303b8cfc0f294a9c4046eea8f0db337b3394ce83e70529f3eb8efc0c0d4d381c6900000000000000000006fd32b6e475288c4c381c606c0f494aaae7fd759ac75dbfb5a85b8fc9d93fd3646ea5dc0c0d5d4820143900000000000000000018adb7a4ac87d83c5c48201431bc0ee94aab4a34d3e7c3d91c187d78b6545583637987dd6c0c0d4d38193900000000000000000000221b262dd8000c0c0df94aac5d4240af87249b3f71bc8e4a2cae074a3e419c0c0c0c5c41982529ec0f9059f94aaee1a9723aadb7afa2810263653a34ba2c21c7af90140f845a04f025435256321a0ae3334b421d2dfb60520d2bdba7701059ff06fb7f4754e42e3e21ba00000000000000000000000000000000000000000000000000000000000000000f845a06e9e84ae7b8edc848e54262afc59a03baa298e039f2917f989989f0639ac12e4e3e21ca00000000000000000000000000000000000000000971580b81199161e2681fe3af869a0e8370f8e7f793f91c2b6e49010bf52dbaaf58e8f07d988605364509f42c3ba3df846e21ba000000000000000000000000000000000000000632f83b3a8a6dd6b3a8e8c2300e21ca0000000000000000000000000000000000000006324e1c7b0ca365349d94c1828f845a0fb65d29cf96abc7a12646808930270725f963bf8cf4a5af80f8ad438c187a4e8e3e21ba0000000000000000000000000000000000000000003780cc48bc9e13378356ac8f90441a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000000ea0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000011a00000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000015a00000000000000000000000000000000000000000000000000000000000000018a0000000000000000000000000000000000000000000000000000000000000001da0000000000000000000000000000000000000000000000000000000000000001ea0000000000000000000000000000000000000000000000000000000000000001fa003cb0006519beb269e6d0b397340a704011d3923c6c9d3286b06ff8b33437ce1a0087c10145799fe03a38a6e7ef91cdb9ae5473267d413a046dd5e84b80ee9d6b0a00a3aff3e51479d2994c9e730ccd79248689e13938ccabd921c0825a5bc4ea151a013316eea440f62b057a7cb4124abadfd7e5d62103e506e3bb5b58baa24c834bda01aecba4ebe7a4e0673e4891b2b092b2228e4322380b579fb494fad3da8586e22a024ea3a70e6554e54fb42d5581a0588f07c5f950d7669c377ad2fa0c30cc7dec6a041660f896507c0d61826eb173be1231662684344d3f8e2e45edd87409dd68bfda04fdcdd851f57f54cc11880efc989407936a09d270df6b7150f9d3215a1492b79a0637f1900d714d8240f11e76c1668505f8edd1ae1e087ba01a6e53d88cde04513a067a26e0766ead2f527810d7475bd1037ecf96e4c7520f75e1e0c887356a16111a06df8a2103564f8c1191b90129e821e9160df5e528ae9ad52fdc5e077349c11e8a06e93b9e676f44adb30f78eff514d0e45730c6213c6385e999e872568aa3711eba08e0430c7f2f6361648b5315cca00d62170e488667dce19185fbb0dea1cd45d1fa0c44efbb32cad5cd0a322d840f6cf4a56c87a05cfbfa8514d036a1fd89a12816ba0d53ff65c502fb8d4ae0b7bc70542a2dca8a2dc122df4c232a02acdfd6e6a2fcda0e469febafc2e9b166ae3ac7542fb9d9ced5de878defd8033d44188b51a0099c8a0f56408d23e6790fec5453738cf042a4a3ef7ec36e9ceae8978e4ffce8e903bc3a0f800ab3c6019c234c0cd9e10d337b2fa51dcb3800bc9bfa22e41d22bc7ca3ceba0fa6c4e1c9df36b7728428843169d6f98510c692a471f46a598282ba5eb68a4d8a0ff7875bfa388daea06ccd6779b39a3681af6827624c3575d70cd11afb58a757cc0c0c0da94ac4c6e212a361c968f1725b4d055b47e63f80b75c0c0c0c0c0f9031494ace8e719899f6e91831b18ae746c9a965c2119f1f901cdf8afa000000000000000000000000000000000000000000000000000000000000000cbf88ce25aa0000000000000000000000000000000000000000000000e5ba528abe5c8c7f6f1e25ba0000000000000000000000000000000000000000000000e5b56faab79424912f1e25ca0000000000000000000000000000000000000000000000e5b08ccab0cbbca2ef1e25da0000000000000000000000000000000000000000000000e5aba9eaaa0354b4af1f845a03656d717f1360ad295fad31dc461d83ebe41f8bfc2bb20643498245e065cba68e3e25ba00000000000000000000000000000000000000000000001834b8f13c6d4f1cf66f845a0549013f204566681cfdc6c21a5d2423a9f68f4604fd614b428b4b45bc6343fb8e3e25aa0000000000000000000000000000000000000000000000095cb66e737dd426089f845a083b666bce0c808dab81a97f57a1d33c46284daf8560103f2195356dd38ab31c3e3e25ca00000000000000000000000000000000000000000000001093709c1a52ae4e88af845a09a1dc32cb2e3f1c2d69f37f312975cc2caa2ca8736f3e00dbb9b2434da80ee58e3e25da00000000000000000000000000000000000000000000000923cc3a71a91c28fd2f90129a0000000000000000000000000000000000000000000000000000000000000012da000000000000000000000000000000000000000000000000000000000000001c3a00aabb8e6c679c0a9ed5effb02f8649130b66c828c9974e485f91fc45736f6946a033d4bfc1cd4cdb0642f5dce045fdc46c2bb61911f16f94f8fb4d01bfbe49e541a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0485e7d2e1bda975843795347885b3f170cec2b00bb47ff8fc2d80851bcdf24e0a06208b0917dee425fd8d85d69c5ced832c254fd3863f1f5371eb83e18a837fd85a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0bbcf953f93e136b5652c8483e05bf4e38e9b5f3c5675003568d32719f122e105c0c0c0ed94ad2113692f8a7d66c3bd27fb8c0b2f101c2c7c92c0c0d3d211900000000000000000001e7d303304b299c0c0da94ad27827c312cd5e71311d68e180a9872d42de23dc0c0c0c0c0f294ad31c2989b00aeaf49fea8998dbf095ff89d9602c0c0d4d381e8900000000000000000000040bd3ef716c1c4c381e80cc0f494ad35bbbd3e34939017a37dbfc0f3d402d41d014dc0c0d5d482011a900000000000000000003e7fcb7f02dd33c5c482011a15c0f83b94ad3b67bca8935cb510c8d18bd45f0b94f54a968fc0e1a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f494ae134de48af0f087472b89b042952e09407e40a1c0c0d5d482013990000000000000000000076fe16b012731c5c482013911c0f8e394ae2d4617c862309a3d75a0ffb358c7a5009c673fc0c0f898d22790000000000000000616bfa9c6b69f00e7d22890000000000000000616bed23e9fce8164d22990000000000000000616bdfa9cc5c0c3d9d22a90000000000000000616bd3728552a1b92d22b90000000000000000616bc73c0c632124fd22c90000000000000000616bbb04c559b6a08d22d90000000000000000616baecf1a841ffc9d22e90000000000000000616ba297d37ab5782f0c527831a14fdc528831a14fec529831a14ffc52a831a1500c52b831a1501c52c831a1502c52d831a1503c52e831a1504c0ee94ae5dde433888a7456c3e0aecbb2e0a5748cbc1ebc0c0d4d381e890000000000000006d8896912297006236c0c0f294aeee5c50ea49e879359a48576c4e981a59bc5dc3c0c0d4d381a8900000000000000000000033d758c09000c4c381a801c0f84094af1931c20ee0c11bea17a41bfbbad299b2763bc0c0c0e6d2759000000000000000360a10a4ae9ebfc651d2769000000000000000360c48aec04d195069c0c0f294af88ad36427959838ebccf44b1c5d8ce8ee1e976c0c0d4d381b1900000000000000000000a320c3c161794c4c381b102c0ef94af8fcc234fd60a4171efdc9e4440345168a0ebbac0c0d5d48201279000000000000000000220057433343d63c0c0f84e94b028b84783a0381d51dcf0e8ef04b5e502958618c0c0e8d381f190000000000000008cdca9dfb394ec59d0d381f290000000000000008b2092d57a476cec78ccc581f18221ebc581f28221ecc0f294b043ad2dcf8edc981ab69cc527950054ca879cbbc0c0d4d381a09000000000000000000000762611a02af2c4c381a002c0f494b0af27a11d3b0fa69d4933efedf7373354bf709bc0c0d4d381ff900000000000000000000690cd0f700fbec6c581ff821238c0f9033394b0ef04ace97d350e24efa5139d2590d26a61a8dcf901aaf845a00000000000000000000000000000000000000000000000000000000000000002e3e251a000000000000000000000000000000000000000000001e308032fba4d36524372f845a00000000000000000000000000000000000000000000000000000000000000003e3e251a0000000000000000000000000000000000000000000009010b47b07a0c7ad20a6f845a00000000000000000000000000000000000000000000000000000000000000012e3e251a00000000000000000000000000000000000000000000000019a36d6329ae557f3f845a0000000000000000000000000000000000000000000000000000000000000001ae3e251a000000000000000000dffd456b707083300000000000000000e06c1414f6f1d74f845a00000000000000000000000000000000000000000000000000000000000000022e3e251a00000000000027f615b366568062cbd2c00000000000272718f2556e107a43b43f845a00000000000000000000000000000000000000000000000000000000000000025e3e251a0000000000000000000000000688a1197000000000000000000000000688a1197f9016ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000011a00000000000000000000000000000000000000000000000000000000000000019a00000000000000000000000000000000000000000000000000000000000000023a00000000000000000000000000000000000000000000000000000000000000024c0c0c0f84c94b1b2d032aa2f52347fbcfd08e5c3cc55216e8404c0c0e6d23e9000000000000000003681998ef330a563d23f900000000000000000367c6ad4e2f34eefccc53e831b23dec53f831b23dfc0ed94b1b72debde32aa19837890b95e3fe8fc108d98d5c0c0d3d23a90000000000000000000039f78e6b38ab5c0c0f9012994b2ecfe4e4d61f8790bbb9de2d1259b9e2410cea5f847f845a00573e6c3f75fccf4cc7f76ecdc09c583262996211572c033f8a883502f37bf01e3e20da00000000000000000000000000000000000000000000000000000000000000001f8c6a000000000000000000000000000000000000000000000000000000000000000fba0000000000000000000000000000000000000000000000000000000000000012fa0000000000000000000000000000000000000000000000000000000000000015fa0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca038b78f65113205a84534609a8f50d048573843947c3707fed6d8f33385501fe6a03dc40693fb3bf64b979c0e0b720cba651945e1a1c67007f82f8a170b92d7faf8c0c0c0f85d94b300000b72deaeb607a12d5f54773d1c19c7028dc0f842a0087371c27b87233e7918c6381aa1f04f93b449f9ba5d0f9d1057bfb75da88479a089d01902a77e9ad7f7103ded15feb0071b100f807050f8feeaeeed16a845d921c0c0c0ef94b3aee9e08c333d59406e56959cd63727cb5aae5fc0c0d5d4820114900000000000000000002bf5ed66db8816c0c0f902c694b40865a6ed718f57468cd3f4f60825a130b89a51f9011cf845a0000000000000000000000000000000000000000000000000000000000000000de3e214a00000000000000000000000000000000000000000000000000000000000001618f845a03bba9108b904cfb969b317a6a0847c2d13a92bb924e87f843935fe7b8f315911e3e214a0000000000000000000000000000000000000000000000000000003904ef5d027f845a04344f879482970d41fbbbc31133bdc9f73172c700eaa6faab1c53fe76bdaac61e3e214a000000000000000000000000000000000000000000000000000038d7ea4c68000f845a06b8c06e5a8d9639de2c13b3e8f375241846e6839c3042ca985e9487ac24aa3c7e3e214a000000000000000000000000000000000000000000000000000b3ef4a469f39c9f9018ca00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000014a045fb090341396a0fcacdccaaeb956cbe22170a2c413f5b36791920012135634da0bbdb6c46a65073920ce1493e5770cf927f61df994d2df4d42ef47a5eb0452f16a0fb437cb06df854b0e9a5f9c071f54ee63d1407c751de4e81c7cb3a621a923fa3c0c0c0f094b41f901c022aceb538c7db67da458dc1f03b2110c0c0d3d2619000000000000000000054f3ae5d5344fcc3c2612ec0f294b43984074fff6e0af7fd717bba78fa31bc69b65ec0c0d3d2229000000000000000001fec1cb464e487fac5c422827312c0ee94b45cc5d4abcf4693b648aa171a1913b15c2345abc0c0d4d381e09000000000000000000323c1b0bd98aa17c0c0ef94b4d4f697405b83aece6bb0b265d7755517607935c0c0d5d4820136900000000000000000280e4bcf092bca39c0c0f8ab94b528edbef013aff855ac3c50b381f253af13b997f890f846a011ea15c3ce93730f20bb9cd4a213d501d858af90bf49a7059634239d81e1ac89e4e381b9a00000000000000000000000000000000000000000000008f8954e24b7bed1633ef846a044a6c51a1742636c5f52a567e08612f0b1f580be1e93cc4318bbc864b75b21fee4e381b9a0000000000000000000000000000000000000000000072ef2ee1ce41fe8796b5ec0c0c0c0f294b53b99ecdb5e3e6c5ba60c04292d225c766435d9c0c0d4d381c4900000000000000000000704af355eb4d6c4c381c401c0f84e94b685760ebd368a891f27ae547391f4e2a289895bc0e1a00000000000000000000000000000000000000000000000000000000000000001d3d235900000000000000002f29baeb2409b9678c0c0f294b69f393936891243fe5b459e32f84bf7b79f55d8c0c0d3d22590000000000000000021038d63f325ccc3c5c425820228c0f294b6accfe5a01a868afb5b76f71178978b98729b1ec0c0d4d381a59000000000000000000003b9d743a5e07ec4c381a562c0f294b72fc3efa90079a37b5b958419a1fc66939a65b3c0c0d4d381fa90000000000000000000003bfbc14f78a8c4c381fa07c0f294b76403a587cf79b8817f6034f3bb99d9013e3bb6c0c0d4d381a690000000000000000000dfd4fc930fa9bcc4c381a61ec0f094b8430c2c0098e933c5d83abb1866c941328817acc0c0d3d204900000000000000000000ddfc462db608bc3c2042cc0f094b8667563548726af6732c799e066c27e3e1efe62c0c0d3d247900000000000000000000279e9fa57e646c3c24707c0f094b8d0633c0b02e2fcb369e06715d073c26c3c82d5c0c0d3d239900000000000000000000a1239016013f1c3c23914c0f594b8ff877ed78ba520ece21b1de7843a8a57ca47cbc0c0d4d38188900000000000000002933a7c4323ad9c8ec7c68188831996a3c0ee94b9865f0a2b6b2536fd3c4c494120043d637282b0c0c0d4d3819b900000000000000000001e0bdd1530f9e2c0c0f494b9d0132ad2147d62c86ff401b488ff05d164d54ac0c0d5d482010790000000000000000000dddcead8b4cfdfc5c482010744c0f9015494b9dce40ac352c84ca11f94c3ce8ec37b747fa658f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e26ba0688a1197000000000001a24573153c793e6800001898faa1d0e7c15f6fa16aa7f845a00000000000000000000000000000000000000000000000000000000000000009e3e26ba00000000000000000000000000000000003514f6566cebf1bc72298f40b8c0518f845a0000000000000000000000000000000000000000000000000000000000000000ae3e26ba0000000000000000000000614520683202a931bc8ce8ef13c0317ab73222d847cf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f87e94ba1030459e75f6041f938c5470f4e0f6468d5253c0f863a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a0d00bfb29ca3e97671eaccbe9ff0f65231aa99da580a6636b674ae752bf0c5cd4c0c0c0f904dd94ba1333333333a1ba1108e8412f11850a5c319ba9f90354f845a007c9a400ecc2eebc565cfc2d20feeb7918c3db214452397115bd5b87f3a55881e3e208a00000000000000001de5fca3bb379d24400000000000000009dfe94f47bc6aec8f845a018c88b4e60515ef72c23539b62239eccb14b91dc549674c82804dd71609edde6e3e208a0000000000000000000000000005dbd0f000000000000000000000000007be61cf845a02cafc07baf118f0b41f7995537587843f281cea80518f05b7c5640d3078c9937e3e208a000000000000341134521b34020ebc608000000000000000000000324c96c4adaf845a0618892da266eb69cf8eb5ae04d271c1f10815eaf33e9530c9767a4049e6cd84ae3e208a000000000000354e206d1435a09c4de2c00000000000000000000033cba5f6710f845a06b9cbc6b73e21fd9bc53d596b85366ea95a7d6b79c2a023cd093664a3a02dd11e3e208a00000000000000534189bfdd29442152b0000000000000545ca060a6df79a0dd8f845a0806f95c6c03f580ef5eb6e158a5a8b6115094c7f98fbc837989b35d307298f07e3e208a0000000000000000000000000000000000000000000000545ca060a6df79f7e6af845a09c2bd49637b09bb19d305ef2a3256da5af7f527123146adfe40e24b91fdbd36ce3e208a00000000000123debcc322c7b653d796d00000000001200b5325e34bb48159e24f845a0a197b6ae0044c90610b63b51392eb7144f4390bba5cb14eeeb9a37521cf0dd8ae3e208a000000000000000000000000000000000000000000000000000000014d1af62b5f845a0a274010baffefa384e4ac371badf58bca8d20e332f0f733fe7dd0e9c16ac2377e3e208a0000000000000000000000005310bb3a9000000000000000000000005e8aa9483f845a0bf689e24506b4e2538defc573210ae5359b2d843bbdcf15f1f50bcf519c49792e3e208a0000000000000000000000000005173d8000000000000000000000000003a36f3f845a0cf10c860f03dc44636de67dec1730250e4b90dfddb0ff1f8fe0d2f1e75a5b1e8e3e208a00000000000000000000000000000000000000000001503315b56b84675f08529f845a0fff9795d68180769da5da6823aa61ce98c2583c5905188dffc9c5a9e2a63fc3ae3e208a0000000000000000000000000000000000000000000000000000007060bfdffd7f9016ba00000000000000000000000000000000000000000000000000000000000000007a00dfa2ee427b421c051d292da37e8460226427485cdffe9d500aca38f183ce207a00e476ca10c29101f2290899d7f5241021a1746454d48735c8d52a4add14e3b82a04bf6b1b93044fab8fcdca37f8f6796e8535fdfcff73340b7bc2b86610393fda6a0b31853c56ad924d32a840844f595dda9cff91d27ef81bfdcd87dbc54c7c45311a0b6ff0d2bdefa3725dc0da1016ab284bb380197eaff3529cafadfefa431121c7ea0cdc72f7cbf91ffd23fc50ffc94c9daf41b37446291f3f2c4b6b3ad6273ff1f8ca0d95a51339aca4132b1d22675f2b4141e4f7c71bdd8cc621825924685d1a93ad3a0daf9cc1810cd2c0a7e7d5984f6803ce8214d86835f739246b8634738b7ac93afa0daf9cc1810cd2c0a7e7d5984f6803ce8214d86835f739246b8634738b7ac93b0a0daf9cc1810cd2c0a7e7d5984f6803ce8214d86835f739246b8634738b7ac93b1c0c0c0f294ba9a3c0a22baebe8c4926227bde32d6edc0d5d28c0c0d3d2199000000000000000002d4cfe402028a60bc5c41982a262c0f90e1494bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcbf901f9f847a02983fce370821ae790208bad5f2f1c8885852c62cfafd26825794144088bba43e5e482010da0000000288b82d6ff969e88f572ee2b34000000000002aff414f2d170414ab34ff847a02983fce370821ae790208bad5f2f1c8885852c62cfafd26825794144088bba44e5e482010da00000001c38d64d449536b74c6d48bb79000000000001df2bde0a7274c60fd080f847a02983fce370821ae790208bad5f2f1c8885852c62cfafd26825794144088bba45e5e482010da000000000000000000000000000000000000000000000000000000000688a1197f845a060c12edcccc16be024210a44e5eb7eca9457e6fce839d511c6e9263082b76ad6e3e204a0000000aece6f59361ddbc7aac287348000000000000b74c3b69d108e114ffcc2f845a060c12edcccc16be024210a44e5eb7eca9457e6fce839d511c6e9263082b76ad8e3e204a000000000000000000000000000000000000000000000000000000000688a1197f847a0aab7517210a335e22c46861d875132b3f597f0d2fb4da60abdb3665953df0627e5e482010da0000000000000000221a777e9a4c419930000000000062c2667546fec46501427f845a0f1d1adf8a778765e14f267061a4d966f2bf165b342e2c73111d88e5145414cf3e3e204a000000000000000000000000000000000000000aece6e8574020cfabd21873480f90bfda0014e88cdcec5072e0f7058e16e9eb972ca7bbd5502a9f70d1abb896bcb8c3496a009cbcb9fc4459cb8933dad695db393378b4cdc1dc4e15646d7ca23a049870e2ba009cbcb9fc4459cb8933dad695db393378b4cdc1dc4e15646d7ca23a049870e2ca009cbcb9fc4459cb8933dad695db393378b4cdc1dc4e15646d7ca23a049870e2da00a6e7835878e4f7bc2a0b5ddb8eb0abbf054ce8476e5ee51eb381fd60e372a3ca00a6e7835878e4f7bc2a0b5ddb8eb0abbf054ce8476e5ee51eb381fd60e372a3da00a6e7835878e4f7bc2a0b5ddb8eb0abbf054ce8476e5ee51eb381fd60e372a3ea0100ca93e729bac9ad357392bd2cbeec6ce5c116219abfc02e7ca9ea18fe8930ca0100ca93e729bac9ad357392bd2cbeec6ce5c116219abfc02e7ca9ea18fe8930da0100ca93e729bac9ad357392bd2cbeec6ce5c116219abfc02e7ca9ea18fe8930ea015387e2f86595f853b2991e9e06726ac173e446b72141a15ee2ee8259f08c282a015387e2f86595f853b2991e9e06726ac173e446b72141a15ee2ee8259f08c283a015387e2f86595f853b2991e9e06726ac173e446b72141a15ee2ee8259f08c284a015387e2f86595f853b2991e9e06726ac173e446b72141a15ee2ee8259f08c285a015387e2f86595f853b2991e9e06726ac173e446b72141a15ee2ee8259f08c286a01edbac83ea6d707ad5846936e45b9183fa039f90a84a7c7d51d3fd61dc4cc726a01edbac83ea6d707ad5846936e45b9183fa039f90a84a7c7d51d3fd61dc4cc727a01edbac83ea6d707ad5846936e45b9183fa039f90a84a7c7d51d3fd61dc4cc728a01edbac83ea6d707ad5846936e45b9183fa039f90a84a7c7d51d3fd61dc4cc729a01edbac83ea6d707ad5846936e45b9183fa039f90a84a7c7d51d3fd61dc4cc72aa024cb18d68d0cfe9a2bb9bfc5f08613e92ac9d2b1fc7ba64727074d4fd4eb55d4a024cb18d68d0cfe9a2bb9bfc5f08613e92ac9d2b1fc7ba64727074d4fd4eb55d5a024cb18d68d0cfe9a2bb9bfc5f08613e92ac9d2b1fc7ba64727074d4fd4eb55d6a024cb18d68d0cfe9a2bb9bfc5f08613e92ac9d2b1fc7ba64727074d4fd4eb55d7a024cb18d68d0cfe9a2bb9bfc5f08613e92ac9d2b1fc7ba64727074d4fd4eb55d8a02b09c537d7f61076857476574e8f684218d23dc4f9d6fdf7873f769bd40cc28aa02c865c6b862372f86793aa2cc9ea0467f0a047d1d115107f95883d45bff01603a02c865c6b862372f86793aa2cc9ea0467f0a047d1d115107f95883d45bff01604a02c865c6b862372f86793aa2cc9ea0467f0a047d1d115107f95883d45bff01605a02c865c6b862372f86793aa2cc9ea0467f0a047d1d115107f95883d45bff01606a02c865c6b862372f86793aa2cc9ea0467f0a047d1d115107f95883d45bff01607a05c11e8aca61e714c59b41edd08a1c7ddb200dc95fd1850c87557f11b07a0735ea05c11e8aca61e714c59b41edd08a1c7ddb200dc95fd1850c87557f11b07a0735fa05c11e8aca61e714c59b41edd08a1c7ddb200dc95fd1850c87557f11b07a07360a05ee4ce392a8259f279816dd11ccfee0b6187441c55f612819835e5c7f81db140a060c12edcccc16be024210a44e5eb7eca9457e6fce839d511c6e9263082b76ad7a0730220c4429e85d5222eebaec14f8d0afaa627295fec6c3f4e88e436df75adaba0730220c4429e85d5222eebaec14f8d0afaa627295fec6c3f4e88e436df75adaca0730220c4429e85d5222eebaec14f8d0afaa627295fec6c3f4e88e436df75adada0730220c4429e85d5222eebaec14f8d0afaa627295fec6c3f4e88e436df75adaea0730220c4429e85d5222eebaec14f8d0afaa627295fec6c3f4e88e436df75adafa07d7d1b8041fb3bf7235bd4f4685b96edab920138b7939763f9de9d190bf3b60ca07d7d1b8041fb3bf7235bd4f4685b96edab920138b7939763f9de9d190bf3b60da07d7d1b8041fb3bf7235bd4f4685b96edab920138b7939763f9de9d190bf3b60ea0999d5bf78123a7d7adbc843d9fa78fc049b1c3b4ebbee62d94b3d47e1bfc7b3fa09a2ec4ad825cccad0e3df2604489ca6733b296d5b21ebcf3fe4e7b033e40edd8a09b70cba3c51d5e03d0509012d4ca17c701a6b9b33598946c13313c68a985a852a09b70cba3c51d5e03d0509012d4ca17c701a6b9b33598946c13313c68a985a853a09b70cba3c51d5e03d0509012d4ca17c701a6b9b33598946c13313c68a985a854a09b70cba3c51d5e03d0509012d4ca17c701a6b9b33598946c13313c68a985a855a09b70cba3c51d5e03d0509012d4ca17c701a6b9b33598946c13313c68a985a856a0a1b332bc5e90cdb8d3550421b625d92a21c09812b23a0e29239d57c120f3b5aaa0a3088256c79a919c2204737cfe51f142ba9e88acc68d1dc03efa3dc828bf9df4a0a3088256c79a919c2204737cfe51f142ba9e88acc68d1dc03efa3dc828bf9df5a0a3088256c79a919c2204737cfe51f142ba9e88acc68d1dc03efa3dc828bf9df6a0af5f02fb405ee52198ad944841235017894e4a1f2f9e57ce42e6df356ed67f5da0af5f02fb405ee52198ad944841235017894e4a1f2f9e57ce42e6df356ed67f5ea0af5f02fb405ee52198ad944841235017894e4a1f2f9e57ce42e6df356ed67f5fa0b180ec50a97fa320faf31c657698203cd2b9fa7d7953454f7e2c9eab019c8ffaa0b64a4ce6d3654d1e76d49fc82617aa6c045883d11ce567e50569ac978546e7f8a0b964268a08a58762c4353a7c9bba397c8bed4cc752e32bf941852f34c4f1190fa0b964268a08a58762c4353a7c9bba397c8bed4cc752e32bf941852f34c4f11910a0b964268a08a58762c4353a7c9bba397c8bed4cc752e32bf941852f34c4f11911a0b964268a08a58762c4353a7c9bba397c8bed4cc752e32bf941852f34c4f11912a0b964268a08a58762c4353a7c9bba397c8bed4cc752e32bf941852f34c4f11913a0d1769b32dc40bdfab31a687a4a2ec4d28477dc25514665f82c096f0b5eac0049a0d1769b32dc40bdfab31a687a4a2ec4d28477dc25514665f82c096f0b5eac004aa0d1769b32dc40bdfab31a687a4a2ec4d28477dc25514665f82c096f0b5eac004ba0d1769b32dc40bdfab31a687a4a2ec4d28477dc25514665f82c096f0b5eac004ca0d1769b32dc40bdfab31a687a4a2ec4d28477dc25514665f82c096f0b5eac004da0dc0f67ab076b0af820d51e0734250f2ed6b92fc137325782d20ae31463d01d2da0dc0f67ab076b0af820d51e0734250f2ed6b92fc137325782d20ae31463d01d2ea0dc0f67ab076b0af820d51e0734250f2ed6b92fc137325782d20ae31463d01d2fa0dc0f67ab076b0af820d51e0734250f2ed6b92fc137325782d20ae31463d01d30a0dc0f67ab076b0af820d51e0734250f2ed6b92fc137325782d20ae31463d01d31a0de2b9900ea527d046a7bd50e5531b5c09aee5690bd465f5218b87e1862fa8eaba0de2b9900ea527d046a7bd50e5531b5c09aee5690bd465f5218b87e1862fa8eaca0de2b9900ea527d046a7bd50e5531b5c09aee5690bd465f5218b87e1862fa8eada0de2b9900ea527d046a7bd50e5531b5c09aee5690bd465f5218b87e1862fa8eaea0de2b9900ea527d046a7bd50e5531b5c09aee5690bd465f5218b87e1862fa8eafa0e022014e7cbd5faefd0e7dc8754427aebb27f4d5bb4792a8400638874a210b0ca0e022014e7cbd5faefd0e7dc8754427aebb27f4d5bb4792a8400638874a210b0da0e022014e7cbd5faefd0e7dc8754427aebb27f4d5bb4792a8400638874a210b0ea0e4f45349da4b4952be89a2fca9b67b521edffed4ca3f6fdf3306187962ec513aa0e4f45349da4b4952be89a2fca9b67b521edffed4ca3f6fdf3306187962ec513ba0e4f45349da4b4952be89a2fca9b67b521edffed4ca3f6fdf3306187962ec513ca0f16c651a8235081ec7600d9a8a02b1a0ef2237a422beba88901c7c9624fa414fa0f1974be9d7fb96697dbff41eff9f6cf3631ebe12e6bb8bddb0d0c5b383bd8ef5a0fa8529202f11d7ec3740650be296985b41436be1e7e7959945f145e1f977cf1fa0fa8529202f11d7ec3740650be296985b41436be1e7e7959945f145e1f977cf20a0fa8529202f11d7ec3740650be296985b41436be1e7e7959945f145e1f977cf21a0fa8529202f11d7ec3740650be296985b41436be1e7e7959945f145e1f977cf22a0fa8529202f11d7ec3740650be296985b41436be1e7e7959945f145e1f977cf23c0c0c0f9028594bbcf554cfadfe4584245b28123e069dfff519fa0f9011df845a0000000000000000000000000000000000000000000000000000000000000000de3e20ba00000000000000000000000000000000000000000000000000000000000000078f846a0cdd00605a828d63894f25a63cf98a42dbab3dc3119afdb11f80e971400cd62cce4e381ffa0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845a0dba6406710d5b070b1e40f4435fdc77fed8c298350a49fd73bfe9f28aa3021bee3e20ba000000000000000000000000000000000000000000000000a3dc408f990f76a41f845a0eb5ad7fd2957a64d35cb4e2a0839327871057e6783eeef0101829de38020a067e3e20ba0000000000000000000000000000000000000000000000000514a26f8136cd8d6f9014aa00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000008a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ea0000000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000013a044d09c05649d869c9b296f59c0dba4e9357274b923c0c22684affaf69581fb92a04aafdfb9e8791872f6c3a39c17837c3c7a9c8fdaf7f0cc7678d2cdb30b109cb5a0b3c5ce9f281976d53af4e2aefbefed86e204bbf9aad878428fb924f0953b0cb4c0c0c0f9015794bbebd57cbf4e511c443736fd2d255f6fc97a833ff8d8f846a00000000000000000000000000000000000000000000000000000000000000008e4e38183a0688a1197000000000001b32b5058671576270000000000000017d8cab133ddeaf846a00000000000000000000000000000000000000000000000000000000000000009e4e38183a0000000000000000000000000000001994bbaf633c11276313fa16ac968a0bc18f846a0000000000000000000000000000000000000000000000000000000000000000ae4e38183a000000000000000000000000000000000000460dbe63bede9972926d803d86450f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f494bc8a0f5e41857292d12ef620e2a5895ce65a5ce0c0c0d4d381ca90000000000000000f120744870bdc1882c6c581ca820c44c0f85d94bcb4e4bcc41ab1494a3eb3456ed4edb8da5d46e4c0f842a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002c0c0c0f85d94bd3fa81b58ba92a82136038b25adec7066af3155c0f842a00000000000000000000000000000000000000000000000000000000000000003a0c59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0fc0c0c0f294bd4f6966e3438251c9e6d2b1ee6f5f742debae35c0c0d4d381bb90000000000000000001c46c9e4b096c81c4c381bb02c0f9014e94bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9f9011ef845a041ce5b871c5fecc02a51d9ef7ddeff5698f770b5eeb37678975c424ebe93cf23e3e21ba0000000000000000000000000000000000000000000a1539d3012afc86e48a3e1f846a0a6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49e4e38180a000000000000000000000000000000000000000000000000000763bdf1356d499f845a0d5021e00f3f9ad6fdf398a54facfed680ef0225cdb575de7f274291eeb48534ee3e21ba0000000000000000000000000000000000000000000a8f098b75da23f2873484bf846a0e41e474b0f9679d35875f98ca4229f64da069cb2154397d286e4e19139b3cbade4e38180a000000000000000000000000000000000000000000000001cc50306a703ccd99dc0d4d381809000000000000000223b8e081320d1f71bc0c0f9015794bdc02589ef451396ba41fae280dbe5ccd19c254ef8d8f846a00000000000000000000000000000000000000000000000000000000000000008e4e381dda0688a1197000000000000599108d865795337000000000000002c23872eddb87cf846a00000000000000000000000000000000000000000000000000000000000000009e4e381dda0000000000000000000000000000000149951b6ab89d8747e4e53f89c796444bcf846a0000000000000000000000000000000000000000000000000000000000000000ae4e381dda0000000000000000000000000000000000009992e08b97ca4e036a5a38e1a5cbcf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0ee94bdd89ea2cdd522f727b46e39aa518f6bc7541743c0c0d4d3818c9000000000000000000120ef3cc8b47917c0c0f294be3e915de846767ac379e0fcaabc06fb4e9569d6c0c0d3d27a9000000000000000000000000000000000c5c47a820152c0f901ba94be9895146f7af43049ca1c1ae358b0541ea49704f8d8f846a04a6546822cb6f658b6190c162580ab1437ed4daf8b47da4496c410e57272f7c9e4e381f9a0000000000000000000000000000000000000000000000003509a98b1613f2630f846a0a1a3d4d3186308ed9ecf84298f55d261c894c3a4efc53c46241acc7cbee10fcce4e381f9a0ffffffffffffffffffffffffffffffffffffffffffffffffff1912d829997ffff846a0bcade124ef8284d5c3d1082dc7d2fdd3505132ff30f7d2c77901cf564496c763e4e381f9a00000000000000000000000000000000000000000000000000093b0c72c18aed4f8c6a00000000000000000000000000000000000000000000000000000000000000001a010d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390ba054e90f8f697f70d3073cc6b21c5d9694b40f346ff3cda150122ba99bdb1585a8a06a65c2750b6ce96ae7d01a811cd7dfc2f2a2491b047f29924849b3b15268b437a07050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3a0a7e7c6de3403f57f907d83f4ff7270f1db6606646146303d47116d6832d36f47c0c0c0ef94bea9f7fd27f4ee20066f18def0bc586ec221055ac0c0d5d482012c9000000000000008d0e164878ddfd0423ec0c0f9012494beef01735c132ada46aa9aa4c54623caa92a64cbc0f90108a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000015a00000000000000000000000000000000000000000000000000000000000000016a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec475a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec476a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec477a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec478c0c0c0ed94bf0a610404baa9d9c9843db582b1157db1355f39c0c0d3d2359000000000000000000005486b2ac7f000c0c0f9165394c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f90ff2f845a0066ed9c225804c48da3ca94afc0896c50e7e287f4837a09a7e9fc0d04a63854ee3e236a000000000000000000000000000000000000000000000000005e5ef5142e95769f845a006a9252c6538079518b2b352a6ce6548cf0b6b07fa7b705bdd31a9f476041f68e3e265a00000000000000000000000000000000000000000000000000ee63c7b5ff47af3f846a008c449fdfc18cbd1fe3a9b3df38017c8d277fe294b54c03f65f196fd369260f2e4e381dba00000000000000000000000000000000000000000000000027ffb1862f88a9e8ef847a00cb865ff1951c90111975d77bc75fa8312f25b08bb19b908f6b9c43691ac0cafe5e482013ca00000000000000000000000000000000000000000000000b8e813469eed3982e7f847a00e06ccf59ca9d931a29fa5f7c0b8083b0a8f23ad7e3d4c9da08471f9e5c43b77e5e482013aa0000000000000000000000000000000000000000000000000053442deafaf27d1f845a019b801df7d999cea8e6d7d7f7b2b0bb8ff07cb169b5922c61a5601805d4e5aa6e3e224a0000000000000000000000000000000000000000000000027f5d642c6dbcca3dff845a024451c947f191ba0b530f793257e0d96a35841b777a0fe4fcdd85e4f88753b98e3e201a00000000000000000000000000000000000000000000000006c9ff08605a4009df86aa02a306d7595987ab10375e6e054d7489cc80c1cf5351587e21ffbd126573f06c6f847e222a000000000000000000000000000000000000000000000002af8d4893991ef5393e38185a000000000000000000000000000000000000000000000002afc891c4f6cf6d393f846a0308e039d225c7d5843e449dddd18a0217a9e77cecd4b8b21297a5d745994a16ce4e381e9a0000000000000000000000000000000000000000000000000b5ee1c2237dc7145f845a0390f6178407c9b8e95802b8659e6df8e34c1e3d4f8d6a49e6132bbcdd937b63ae3e223a0000000000000000000000000000000000000000000000144c6fd2e5bd747596ef845a0450afbadc95be6567831d88455f6a59d850c71c1de4e873bb084146dfb06dd47e3e238a0000000000000000000000000000000000000000000000006c262eec93ac62bc6f847a04d299e0f7464af29a45a350994d64dff17b529d7865327065e4e3d9e8b934c87e5e482011ba0000000000000000000000000000000000000000000000000be17f165955c42b4f845a04f1c9f629bb71aab7d1714da61a9b22e16f92772ad0928e730427024ac5f2b43e3e20fa00000000000000000000000000000000000000000000000001d53f9eb7714a541f845a051c9007cca334123558f0f85f608fa128a98cefc56001cbaad432c54c96adf16e3e272a00000000000000000000000000000000000000000000000016ebf91362eb96346f845a0527c604469ebbe767341beccb5fc5bf6289ffdd59210ea76988145d7ff20d38de3e20ea00000000000000000000000000000000000000000000000015aad205dc2d47ed1f845a052c54c7d408b9ca1cead7ed6a491323e5451549d9ef0992d42f595e36ce08f77e3e26ba000000000000000000000000000000000000000000000000014078de3ac014d18f846a054e90f8f697f70d3073cc6b21c5d9694b40f346ff3cda150122ba99bdb1585a8e4e381f9a0000000000000000000000000000000000000000000000004dbafa132187f9d83f845a05bf0517b0d7cdcf96fc757e4041c30756ba2908355ade5112aa1f97767b9dfefe3e25ea0000000000000000000000000000000000000000000000000132ec8981331331df847a05f14a05004979efb0b63d87957e34df56d04a57ecd44c40ff3ba3f2477694f65e5e4820137a000000000000000000000000000000000000000000000000050bb306e02acdd6df845a061bd8b7d7ea1350e36cb998c4b61b337d0b7e6c625816251eef440c23f9b21a3e3e266a00000000000000000000000000000000000000000000000001e46cf6ead9cca6af845a063080d95ec085011378c4c6d1dfde229de39c228e5471df3ccf065f05bef0f02e3e20ba000000000000000000000000000000000000000000000000016e44754449a0d2af847a069d4b4ad61a248c9c09011fa9f24ebdc295eaab0719dc261fc601f40cffadeaae5e4820141a000000000000000000000000000000000000000000000000006568b5a698fd596f845a0745225778312a78692ae15528abef363d748e20db0ffc5ea6379b04c157a0880e3e20ea000000000000000000000000000000000000000000000000031d654038d425655f8d4a075245230289a9f0bf73a6c59aef6651b98b3833a62a3c0bd9ab6b0dec8ed4d8ff8b1e21ca000000000000000000000000000000000000000000000016c0c70eefbef7f63f4e222a000000000000000000000000000000000000000000000016be462fa2314b81550e223a000000000000000000000000000000000000000000000016b95bd5ac96aa29cb7e224a000000000000000000000000000000000000000000000016b70d4d62771041535e482013ca000000000000000000000000000000000000000000000016b15ecb80b6712ee20f847a078e6348bdf2ad14523bbcce353173548161983034fb9502a2f16285450871c34e5e4820105a000000000000000000000000000000000000000000000000000071b02771a1477f846a080a75eda56e9aa2d42642b3413c0e9464697932fb78e0a3eab0e3d0a6c110629e4e381fca00000000000000000000000000000000000000000000000045c73e2cfc593365df845a0828d961e33c5d8c110d40025a0c470038f9143adb9df2526f11234503cd86a90e3e201a0000000000000000000000000000000000000000000000011e4a4668a5edaf517f845a082925f70a42a8633fa28e1d369e47cc833e0a19d22b9450795322233e965ef3ee3e201a00000000000000000000000000000000000000000000000024dbcd0cf94b27408f845a083f8975b25917b7df8ba465bc82d6b4472b7883d2ee49412f33c76db8bf49be0e3e207a000000000000000000000000000000000000000000000000126c4a57abed78a17f846a0857f56b3cddd78c4aaaf8728697967a1077c02f54da55abdfa04b585ad1a70cde4e38184a000000000000000000000000000000000000000000000000064263e48896de706f845a087490cfb653047f997b69e61c7a1fc30f26358b189fb850deb46079b7f96ff2fe3e272a00000000000000000000000000000000000000000000000084d439be32c3d2095f869a08a0869b48cb256b682d977ea9bc491a3e1d479c22ec392a89df1b845f379cceef846e21ba000000000000000000000000000000000000000000000009aafb728bb2c481402e21ca000000000000000000000000000000000000000000000009ac05abad672f25ac9f845a095eb8b8ef7069f8b5c679fa94eca1f6d19856b88aa03f8eaa9ca2b675d797698e3e201a000000000000000000000000000000000000000000000008eb8f03f3f40e9337ff845a09ede93be0d8fc6a5eb9cf1c7345a85b7519d8487a727aef0c2f00ab966aa7716e3e20ea000000000000000000000000000000000000000000000013800b8c21e6e378361f845a0a1b89e3efbe19d22f0f2de6e743974967781c6b8e779d8458bcae97471cc4afce3e214a0000000000000000000000000000000000000000000000001569964a2a306a768f845a0a94b60751ceff49e15cca86a84cd4efda434ad5c2edb4d38d4fdc8553582b377e3e20aa00000000000000000000000000000000000000000000000007055c8637af4c799f845a0ac920e969edbd20b319b8f09e6c5edb5fdbef36ec770c6e8e8615f7c8dbff7dfe3e266a0000000000000000000000000000000000000000000000000014eaa66a1d78000f845a0aed6c24093e5abdac44c93ae0026006b58f9c9e3e3d3efe28aa052354c1d2ddee3e20ca00000000000000000000000000000000000000000000000001eac6d08bfd9dae9f846a0af136c4fec1e7946aef1b5e16763785d917b705abf974ed4b3d651f3777689cae4e38183a0000000000000000000000000000000000000000000000001b32b505867157627f847a0b440be4deffff278a0a54bece4d5bba517a3236073012092c2f7526f6434fd2ce5e4820142a000000000000000000000000000000000000000000000001659a19cff1f0d8c60f845a0ca44a2617bb49e924f4d410857de307ff46f4a5f397212319f5f108a9483beb6e3e234a00000000000000000000000000000000000000000000000004b89abd1a44a083ff845a0ce923bfef35df4b395299bc115a8bcd2c216db39539fc2860179bf5a8c79c956e3e21da000000000000000000000000000000000000000000000000153e17d8ac9567121f847a0d1d920a751518467a4afbad02d833a0722142520115df52e62a4524258bf04ece5e482013aa00000000000000000000000000000000000000000000000000af7ce6c0f1a872bf847a0d3e3b20fa7319b86dff7d4caea5efb7cb38df6f847944644871774561cbb2479e5e4820107a0000000000000000000000000000000000000000000000000016345785d8a0000f845a0d722500f1a1a9b3f1ad91802a20b180ba05ffe232f9a17da4eddda61a4632ac2e3e26ba0000000000000000000000000000000000000000000000001a24573153c793e68f845a0de3396827fa575355c40f2591e90c244df1fd101730e44da9e54c1d040587739e3e201a0000000000000000000000000000000000000000000000004979cfaa67a39265af845a0f5995e255aa2c800c3136844499a825cdcc83d4178273f5699d957072e2ea32ae3e266a0000000000000000000000000000000000000000000000000040140b5a3377000f847a0f6caf2559869b1744c19aa1ae15b6c45a8d6eea9961e6958111ec18b9b1184e3e5e482013aa0000000000000000000000000000000000000000000000002127c9a0493b06a0ef847a0f6cc6c4222dae194a09f19d368f4b2b85698ddef67e513b005143d861f8ea8e2e5e4820140a000000000000000000000000000000000000000000000001218b8a8c4ef40c5e3f847a0f6edbc4da13df00f9be8b14d0897246ee8b8426ce2aac633ff8e2ef1aed82681e5e4820141a000000000000000000000000000000000000000000000000915cfbd3569d31c4bf845a0fbe94dd69d14ade7bb30a2170823967d3b17cea6812f10ce99d7af34cc1fce88e3e207a0000000000000000000000000000000000000000000000000eec30b1bcde32c7ef845a0fc5da5621ecdf503c1b60d3853c14e4222d580d5bc84c71986837c27fa27ef1ae3e265a000000000000000000000000000000000000000000000006f127b9a5423018dfbf846a0feb42399ca12f84d56ee1233f1ceaa7ba80395fa620c0eee2be7fffb2dfebcfce4e38184a000000000000000000000000000000000000000000000000000460bc121655abaf846a0ff16d5a122c2eb199e8ef2084c012b4487fb7e03147d1078197667329aec5a2ee4e381dda0000000000000000000000000000000000000000000000000599108d865795337f90441a00055db1c41b2272eaef9225cd2750f18a33dcfe06f456a5d8bbff9f2785bd740a005902947d7d27d664fb8adef648d91225cd9af622622bc6017334d64ee900baca01d3fb04fdc6a2d6c977d0dfdc776498014ce6f919157e7dedc0e808359bf42f1a02a9f100b9103993ec2993a2dbe2dd6ab0ef65a4bc285359a6d2f66ad33120716a02c349f31d69293493b8c2ab64bcccd406f797edc7e1212b1bf52c9b880d9357aa033d1d1698be76ca5d91049be950e70ca725f45b8071d46047f3aed98e2ab9b4fa037d920089e11921f3bad8fcb28b6f27623f73b6106c6bfa773de07b1a86ace12a05858aa37184aaf71b925c7b1195e2651f175f2c7b9f554327fbf067105636b51a072c035c75929d7db79209e766c47068b10cba63d27a4398e5acdcc0d293f3931a075d179b340750ce57d2ecf17f98f27e78c742b8df016157c225ee4bb188bd639a07c0512acb854d8e91fe67b506dce9014e9ff1254c53d57c43d236ed5bcdb9332a07fe8dcb6d34fcedcf0ba0a0b5fa06de59c603cbc70e5e412f56789af5956f102a08b11f9ede7572b59be1d054f022f7461c08a14388ce60921f5b80e9eb9ad0417a08cf181065d916b677e13a8b08261f5dd2c87c6a137d26f35c60d201944bb9816a09a6d31c950cf59a1d1d442a6793f5f0dfb71b8df6e52f28b93a232a67238ec69a09d98752c354deebddd53535455198eacf8cfb934237d3523207f70386be5e3dca0a6b50e66a554852d72183d0018a031e0dcab4677a6096b5aaf6b614db4753667a0a72580ecd31d3907f6d97e38f044047d93e81ccbefd09661d34a6d7cf5ed4491a0aa97f9cb2e742b698cf6a3d3aafc16d74f2ed30deffa5ab5483d1e475d26c806a0add657f797c27ca305e224df1a7036a0467f58ed0a61d1fff55bbf4d6348cf51a0b233d602ee7e7852b80874e525944fcf0080ba7000c145d0ba99edc2e7ba518ca0b2940356fd89a84cf9a86227596a2f59f085c679d5f9450737d0ae4daf0efceaa0bd5496627759cada847047c1c161530363af6f4760cd5a05b62f0f2e7d7d4a11a0c269910e79664bb02e4ea6b58a6546a8f30ea3f5840ed79d34d06b85ebddd776a0c43de090e120c56163c78a79947fb033da57ecb0a8775adba22c13a3cf4abf83a0c565b8467ed0be432a896df92668a8397c5e16b4f50e6c53d4929ca811966139a0cc2e8e5cd534eb5e35dabd4cb4b69550879b28082441ee3da8b83455b07b7046a0df20db09d6de1c44987e388a59580c27855c014bc46effb29442a144f5f6ebdda0e12062fee1f3b1b93862cab62f50f623186a70b5189a76998d699bde08be3bf3a0e4a61b47dd0f7a272937ded65a59d4f6d9a0d7e356f5d0001ac052cffa90c191a0ee61682ec263cf9ac626d5a9b0cd5e26edbfa98b5b368b540e65bd739c37e3b7a0fb19a963956c9cb662dd3ae48988c4b90766df71ea130109840abe0a1b23dba8a0fb437cb06df854b0e9a5f9c071f54ee63d1407c751de4e81c7cb3a621a923fa3f90200d20a90000000000001dcd1e0269436549ee0b0d20b90000000000001dcd1e0d754b471539d2fd20c90000000000001dcd1dc89f86174a19c9bd20e90000000000001dcd1dc81b780d1ab2c28d20f90000000000001dcd1dbb7db1c53950735d21490000000000001dcd1e263352803343db6d21b90000000000001dcd1d60969f4824f8ad1d21d90000000000001dcd1d6ddefed9cc64ad1d23490000000000001dcd1d9a47ade57da4ad1d23690000000000001dcd1d98f5602f1a72e05d23890000000000001dcd1dd9af3b791adee05d25e90000000000001dcd1dd1a660ee491a62fd27290000000000001dcd1dd150b5dea092025d3818390000000000001dcd1cd3dcc103d869793d3818490000000000001dcd1cd0d61c41c31c01cd3818590000000000001dcd1d0c1f4d9f739401cd381db90000000000001dcd1cd28af3c0a3ad98cd381dd90000000000001dcd1ccf8b642a168c162d381e990000000000001dcd1cd1f75aa0ba6e962d381f990000000000001dcd1cc2112c4f12b9714d381fc90000000000001dcd1ccdb7ff0607ec890d482011b90000000000001dcd1ccbdfdd48edf1d43d482013790000000000001dcd1cb13aa7751d31d43d482014090000000000001dcd1ce632d38af93e519d482014190000000000001dcd1c9b65a4ed836f4f0d482014290000000000001dcd1c9282d8bf12178b8c0c0f86394c02ab410f0734efa3f14628780e6e695156024c2f848f846a027e994043f913917671926020e61f80e9d344c590cd84a611a4e66eed35d2480e4e38188a00000000000000000000000000000000000000000000000000000000000000f01c0c0c0c0f394c066ac5d385419b1a8c43a0e146fa439837a8b8cc0c0d3d2139000000000000000005939e6c550f99467c6c51383019552c0f9015494c0a6bb3d31bb63033176edba7c48542d6b4e406df8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e201a0688a1197000000000004979cfaa67a39265a0000000012245bc54fade1d07e26f845a00000000000000000000000000000000000000000000000000000000000000009e3e201a000000000000000000000000000000002acbaf92492e7afa61a92b1aa283df688f845a0000000000000000000000000000000000000000000000000000000000000000ae3e201a00000000000000000000000000049911dfc489568006593cf41781eac13550703f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f494c0e53ab8bc6e1f134cc52b02cbe8655d9f562a65c0c0d4d381dd90000000000000000000d770eb1e50532ac6c581dd8205a8c0f494c0f990b2800f237fff5a8874048f2c218343e828c0c0d5d4820124900000000000000000002324e65202ad59c5c482012401c0f901de94c20059e0317de91738d13af027dfc4a50781b066f9011cf845a01bce331f95603049939b113a9b550350bb5211cd73cffcc46c2f247a6d7093bde3e202a000000000000000000000000000000000000000000000002b6d578046631578e0f845a0519b10c201f018d3126f87a72ad07cc05f09a7bd903f0f757351e83cdd370b1ae3e25fa0000000000000000000000000000000000000000000020e0b9d102c639607b400f845a08796f7cd1ae14ad9d18ad1dd756ca9bd5b1ded08a1a029cbdcbf076630e41a31e3e202a0000000000000000000000000000000000000000000070493fa7d149feb1fc378f845a09e4fbd1ca1ca3f2d1ab98938cb11e2cd2408b2b559fed6cb26f4aba2f9860295e3e25fa0000000000000000000000000000000000000000000000174c816309b2976b000f8a5a01122ad22a463736ebcabe1ffc0287ab7db0d9e1995b696d6d5f26ae378d7a5d9a06d12d10bcbfde5f52ed4616b131cb09c52b5d4f0610906a60f7cc538184e5334a086b8c57e0757f0c3a2432a08485d6720a63076399a78b553d6175cea884e19caa0a93c393b9d73c86c9241ec40447c61f731d053e2c00795422397513efd2e002ca0c58d67b91536e945ae318f2f3959fe83fafbf45c20a6916d9e295f488bc75575c0c0c0f83b94c26d4a1c46d884cff6de9800b6ae7a8cf48b4ff8c0e1a00000000000000000000000000000000000000000000000000000000000000002c0c0c0ee94c2727414e591de8ed740980f78c56e1599f5b048c0c0d4d3818e9000000000000000000002d0f320f4efa2c0c0f9015494c28b4ee55c3f3c3a7310ba073ee860d56a11bb00f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e238a0688a1197000000000006c262eec93ac62bc600000008471ea7d2fd6d94c79d1ef845a00000000000000000000000000000000000000000000000000000000000000009e3e238a0000000000000000000000000000000000000a5c2a37f8f7185174d569f11bda0f845a0000000000000000000000000000000000000000000000000000000000000000ae3e238a000000000000000000000000000040f12393aebe397b025f0f5f551e904b189fcf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f294c2ba4f0f29687557bd107357e5b974a6558cbee9c0c0d4d381a1900000000000000000000b7f8dc4f2254ac4c381a102c0f9017894c2eab7d33d3cb97692ecb231a5d0e4a649cb539df8f9f869a00000000000000000000000000000000000000000000000000000000000000008f846e21ba0688a119700000000009aafb728bb2c48140200632f83b3a8a6dd6b3a8e8c2300e21ca0688a119700000000009ac05abad672f25ac9006324e1c7b0ca365349d94c1828f845a00000000000000000000000000000000000000000000000000000000000000009e3e21ba000000000000000000000000000000000000004f101cf9ff7e914b3c039869b88f845a0000000000000000000000000000000000000000000000000000000000000000ae3e21ba000000000000000000000139d6db306e7bd8781cea1ad5910cb2872cf290edf5cf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f094c33023a7b5e9c1d6cc4189ff60d1b582957f8930c0c0d3d26b9000000000000000000011faf7639f66f2c3c26b1bc0f8db94c38e4e6a15593f908255214653d3d947ca1c2338f848f846a0ec3b28e995691dceec552e4da2a5fcb8a2ff5fe83e14a0710683d8dc6512581ce4e38180a0000000000000000000000000000000000000000000000100000000005dcce400f863a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000003d4d381809000000000000000029a86e8450ab27ae8c0c0f9015494c3db44adc1fcdfd5671f555236eae49f4a8eea18f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e201a00001000258025801a9fea7c9000000000000000003200ced8360ee41d9b497f2f845a00000000000000000000000000000000000000000000000000000000000000002e3e201a00000000000000000000000000000000000dce7ae8275d280be648e019bde75b8f845a000000000000000000000000000000000000000000000000000000000000001b1e3e201a0010000006c0000000000000aa725a0331d9b71d135fffcb3e7312830688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000001b0a09b637a02e6f8cc8aa1e3935c0b27bde663b11428c7707039634076a3fb8a0c48c0c0c0f8a994c3f822e94c321dd3ee53ca46b78098ea79b7ec8df88ef845a0e06ca6d6a9f55e10a529892a92452af02e401249d4684cef2254701a2bdb46d4e3e221a0000000000000000000000000000000000000000000000d416d3ee82591147c22f845a0e1953d2330c8ae316ede9f4d1cfa46ef799836a53537bb86686fa94340fc7519e3e221a000000000000000000000000000000000000000000cacf02e01e1458d7d3d543fc0c0c0c0da94c465cc50b7d5a29b9308968f870a4b242a8e1873c0c0c0c0c0f87e94c4922d64a24675e16e1586e3e3aa56c06fabe907c0f863a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000007a04fc648e708d8f69cd7ac3668ac52a76237ca386ea913760840ac4e7cea226778c0c0c0ee94c5bcfed20e3c1146939f29b3942a504a5b081daec0c0d4d381839000000000000000018ade4aa832054ddac0c0f9014c94c6093fd9cc143f9f058938868b2df2daf9a91d28c0c0f90130d2679000000000000000001ab5102d524e8273d381bd9000000000000000001ab58d081845ed1bd381bf9000000000000000001ab608074fc0aec3d381c19000000000000000001ab68306873b706bd381c39000000000000000001ab6fe05beb63213d381c59000000000000000001ab77904f630f3bbd381c79000000000000000001ab7f4042dabb563d381c99000000000000000001ab86f036526770bd381d29000000000000000001ab8d453a65f6cb3d381fe9000000000000000001ab8f6628b52ca5bd48201009000000000000000001ab915f782b52d03d48201029000000000000000001ab934c880dcceabd48201049000000000000000001ab9529560b54c53d48201069000000000000000001ab97053a757b6fbd48201089000000000000000001ab98e0c9efdbda3c0c0f294c610bda7d181a1a4159ad10a6775e7564511b9f2c0c0d4d3818e90000000000000000000008a3c2ac42b7cc4c3818e0dc0f294c677ece487d8843d71a8730507681422b3b982afc0c0d4d381cc9000000000000000000003110246679542c4c381cc02c0f9012e94c71ea051a5f82c67adcf634c36ffe6334793d24cf88ef845a052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02e3e208a00000000000000000000000000000000000000000001503436cfb7c4b872d0e3df845a0a254218e41f18636cb903aa2f0c79bc9acecf420fbf0c6cb721e2d1a76c66852e3e208a00000000000000000000000000000000000000000001503315b56b84675f08529f884a00773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca04fad66563f105be0bff96185c9058c4934b504d3ba15ca31e86294f0b01fd201a0cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300c0c0c0f901c094c7bbec68d12a0d1830360f8ec58fa599ba1b0e9bf9011ff86aa00000000000000000000000000000000000000000000000000000000000000000f847e222a0000100003300330017fd0a4e0000000000000000000406422e542ecb388aff7ee38185a0000100003300330017fd0a4e00000000000000000004063d7a35129e4862ac30f86aa00000000000000000000000000000000000000000000000000000000000000001f847e222a000000000000000000000000000000ad00931b64332253b79cc01a13ec0aba64ae38185a000000000000000000000000000000ad009339d8dd8b461efc0160dff3a644968f845a0000000000000000000000000000000000000000000000000000000000000001fe3e222a0010065520c000028ed9534f75b74428bd3b380be2cfff12fcae39dfc688a1197f884a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000001ea00c27731cac1e121449a18deab4cdfc63afb79524904e9a0a1a4146c3fad6baf4c0c0c0ed94c8262e0d39503e2d9d23ce3c0c9915481205973cc0c0d3d2609000000000000000000002aa1efb94e000c0c0f294c895bdfe2d758a6fd07d7a2ba5dd24a1014e2832c0c0d4d3818590000000000000000000309c9f790fc2f3c4c3818503c0f294c8fc2f2e08844f6e28a7362afd8c847f7925bc64c0c0d3d20e900000000000000000402dcba8d7cc4f6ac5c40e82149bc0da94c92e8bdf79f0507f65a392b0ab4667716bfe0110c0c0c0c0c0f794c94ebb328ac25b95db0e0aa968371885fa516215c0c0d5d4820145900000000000000023b1d2765837579d88c8c7820145831f140ac0f8c094c9ca319f6da263910fd9b037ec3d817a814ef3d8c0f8a5a00000000000000000000000000000000000000000000000000000000000000006a006e19b5b71ac08ff2ded214d51d1a1d50d6141c6df303c8b575e6452c46b6beaa0243a29a879f85d582d7ef566191daaafba060067ab09ea58e58f534fb7fe331da02e7444e1dcf538780ee1ad019a97354d3151a7f5401006c2514689e41277879aa0dda6f4c15f798be1d364c3c8f74af42d33c7b174779f9417d9156b62551558fdc0c0c0f294c9cf82c17133ac0d55b5ec9daca5464260529c56c0c0d4d38183900000000000000000011ef9960ae72a48c4c381830bc0f87e94c9e1a09622afdb659913fefe800feae5dbbfe9d7c0f863a0000000000000000000000000000000000000000000000000000000000000000ba04c7ad7de04cdbbed7ac607cce756ab69f3ce6b2782a5114a9819ccb8d6b7d5dba0f8ad5150ff7987f0464cd848559b538152a0a36ed83b24aa0c11a5e92599ffd7c0c0c0f901fa94c9e25234a08a97b84ba69be5ec3dbae7efceb473f8d5f845a00f802f75b434022f278b7df1a7b27939b475521ad4d6abe1085059b8238dab97e3e25ea000000000000000000000000000000000000000000000000009577b1f00962acff845a09d1bf6e7ab54ead0d5e09cb854c8c99252b8180162427124d61c453c8a0ced02e3e25ea00000000000000000000000000000000000000000000000000000000000000000f845a0c326274d9bb1d2bfba31e78d4ee52df978c3bfa2a4abf03e1d81466889943736e3e25ea00000000000000000000000000000000000000000000000000000000000000000f90108a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000da012661a4849e4a87f27c9e27dc256775758e41d84c741595ef491e8b235b94ff0a049ab0e01a8b680fe30f908310c98990d96cab16d2860fddf6427683cdd7d89b0a0734ebaa36c3d592bdf9b040bb2c4a2898bbc834dcee29f0b053d59f4ab712047a0b77d6cf9e09aa2ef67e67ca879adb2bd657311f78f4a70636f897451e304f8dac0c0c0f9015494ca21d4228cdcc68d4e23807e5e370c07577dd152f8d5f845a042cd46fa0013bf95c0dd67cfc79c9a305e4cb6c006650413fe16b8dfa59c61d8e3e211a00000000000000000000000000000000000000000000000000000000000000000f845a050eb0985276ea25d0507ab28e29eb9bb845e9a09547727e6c7b346719d689645e3e211a00000000000000000000000000000000000000000000000000000000000000001f845a08111a1be5f0890ad24830bea78661e2c2e4a9bdc8521a95078611bd55baf7435e3e211a00000000000000000000000001103c0550771ffb586688ffbba90c6b85ebfc224f863a00a119bdc053a377f14890a6805fb9d8b22717c4e0cfc30127602db182170d862a093712c693d8d2610427a9e76c00539e3b0d15688564068f8bea21f0c26d5e6c8a0d72ac986e14b90cdbbe02e23a8d94b8f10b969959b07328f4e62d27111d958d0c0c0c0f094ca43f78dd8a9c8e96b53dea4e13bda504a0f8c4fc0c0d3d24090000000000000000000087a8eb85b4bacc3c24018c0f094caab29ecae649ae94a24bf7eba52a924ec56935ec0c0d3d24c90000000000000000000074516d2822081c3c24c02c0f094cab50a2b987fe307553c9ef2f4d4ac574a1ba13ec0c0d3d244900000000000000000000c069c8c605081c3c24401c0f394cad97616f91872c02ba3553db315db4015cbe850c0c0d3d274900000000000000000043779ef69b10159c6c57483018115c0f294cb95cb20f4d55a435e6474b9da5ef25f4d870667c0c0d4d381af9000000000000000000000000000000000c4c381af07c0ed94cbd6832ebc203e49e2b771897067fce3c58575acc0c0d3d2719000000000000000656cf1440ad0e816ddc0c0f294cc57297cb89986ae26d27d763fbe575522c67ab7c0c0d3d209900000000000000000151a4ef6c3339382c5c409820631c0ef94cc87b91e911d964805df6915510af1eeccc13552c0c0d5d4820128900000000000000000002386f26fc10000c0c0f694cdd583ce1f021a81388ff04d45836f24a0f26d97c0c0d5d482012190000000000000000000f4cc0186acc9f4c7c6820121820105c0da94cde3eb49d53d932c605efbc1bc425c6affa5eac2c0c0c0c0c0f294cdf060936cb2a58512d4e9860bfc94c1e4dbf485c0c0d4d38195900000000000000000004fe0f25bce3f0ac4c3819503c0f9015a94ce1bec6c2fab43d52c12802e13f481f8326ed2f3f8dbf847a00000000000000000000000000000000000000000000000000000000000000008e5e482011ba0688a1197000000000000be17f165955c42b40000000000010d6ad4c743de45f9f847a00000000000000000000000000000000000000000000000000000000000000009e5e482011ba0000000000000000000000000000000006844a775bb30a1b0b80c1f165f495434f847a0000000000000000000000000000000000000000000000000000000000000000ae5e482011ba0000000000000000000000000000000046be80b8ee5c6df1f314e74fc9bbd6d14f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f9015494cec9f77c90f54ef57acfa773822131dcca60f867f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e20ba0688a119700000000000016e44754449a0d2a00000000000a3dc408f990f76a41f845a00000000000000000000000000000000000000000000000000000000000000009e3e20ba000000000000000000000000000000000002bbcd064feb0aabd473713a534aa90f845a0000000000000000000000000000000000000000000000000000000000000000ae3e20ba000000000000000000000000000000003a4650b8560b118856a478225c057599cf863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f694cf420f085b284fad6b0f5d17d3772c315d11ed80c0c0d5d48201039000000000000000000001643d26c6345dc7c682010382012ac0f8e194cf6958d69d535fd03bd6df3f4fe6cdcd127d97dfc0f8c6a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca058d1ee36a2635b7cdabf6627c9ceeadc55e3dddd558db1b31e1c365e5228cedea06463ab5738278afcafb9b8bcb5b23cc6d342298f0b18756bbdc343ef7dd7f001a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0c51c65a607d170e09469c330237d35e163544dfad7c72dc16691ad981a11ff41a0fc19541bf3335480b9ec331368f41d19b99f9d7928bff4fd4600e6c1a99b2439c0c0c0f294cf7d915b40551130cf585de6df95d4ae62013e4bc0c0d4d381a790000000000000000000e360a566118c61c4c381a719c0f83b94cfbf336fe147d643b9cb705648500e101504b16dc0e1a0b035f62398c2f37b04e1eceb7c8e682f004b880b099118069ef0a8d3cb0fcdaec0c0c0f294d00f88fd0242ea338aa891c34c84296357a53781c0c0d4d381919000000000000000001430194affb14b8ac4c3819107c0f83b94d057b3c39d563bb16b05a3eb8ca8a2fa43c8df49c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f094d099bc9db95489b6a036623fb2c744f40dac983cc0c0d3d245900000000000000000000775ff949416d9c3c24509c0f8ce94d0ec028a3d21533fdd200838f39c85b03679285df892f847a066f6b2e45cb63245a76eec0485a03230238e69c78424d209ada7c9072fa24e30e5e482013ba00000000000000000000000000000000000000000000000000000000000000000f847a0a4e954e7aa230b38643562858f106670f2867a6d26fc141190e0b45577d9f076e5e482013ba000000000000000000000000000000000000000000000d59ece829d886dcf1ea4e1a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcc0c0c0f094d0f8853333cfaffb59336cf4c521648faeba15fcc0c0d3d23b9000000000000000000000000000000000c3c23b01c0da94d152f549545093347a162dce210e7293f1452150c0c0c0c0c0f294d160842a0538dc5605d87c7b9e42a8904b0ac33cc0c0d4d38189900000000000000000002419503efd0576c4c3818916c0ee94d21fdab5bda02aba6c415a98649e529fa22e6413c0c0d4d381e3900000000000000000012dfb0cb5e88000c0c0ee94d2b344b91708e81c19543fefc5f9facaf60fa530c0c0d4d381d49000000000000000000348377f39614000c0c0f694d2c811d48b4cbccf8afe1428fbade11beffb269ec0c0d5d482013b9000000000000000008258391c21c73c32c7c682013b824df0c0f294d398784fbfd611cb3a43825986ed1be71649e553c0c0d4d381b2900000000000000000008d80beafcd95c4c4c381b209c0f9015a94d3c41c080a73181e108e0526475a690f3616a859f8dbf847a00000000000000000000000000000000000000000000000000000000000000008e5e482013aa0688a11970000000000000af7ce6c0f1a872b00000000038c16b563b32006e252f847a00000000000000000000000000000000000000000000000000000000000000009e5e482013aa00000000000000000000000000000000031b64ac74b2cf0fac4304eb27e70151ef847a0000000000000000000000000000000000000000000000000000000000000000ae5e482013aa00000000000000000000000000bb6067de8647bde5e24eba83c0f7704fbe92ccff863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f9019194d3c6cc89e3e260c0e2d47071b31ba1715b1416a9f9011cf845a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbce3e219a0000000000000000000000000bac849bb641841b44e965fb01a4bf5f074f84b4df845a07bcaa2ced2a71450ed5a9a1b4848e8e5206dbc3f06011e595f7f55428cc6f84fe3e219a0000000000000000000000101845adb2c711129d4f3966735ed98a9f09fc4ce57f845a09247b7d6487ac2b73ae4daed6d3093defc3077de1abc059ec7978bb4065925a2e3e219a00000000000000000000000000000000000000000000000000000000100000001f845a0db85417abd40b6e8de40c4dfebdbcfc91c0257fc733e6f49bf7f0c8af81a065ae3e219a00000000000000000000000000000000000000000000000000000000000000001c0d3d2199000000000000000000000000000000000c3c21901f842f84019b83d363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3f094d4b454f6f95d9c66b38383810de0fa8cb7c135ffc0c0d3d24f90000000000000000000075a66745fdc59c3c24f0cc0f9012e94d4fa2d31b7968e448877f69a96de69f5de8cd23ef88ef845a052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02e3e208a000000000000000000000000000000000000000000000000000003e1dd27826a8f845a0a254218e41f18636cb903aa2f0c79bc9acecf420fbf0c6cb721e2d1a76c66852e3e208a0000000000000000000000000000000000000000000000000000007060bfdffd7f884a00773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca04fad66563f105be0bff96185c9058c4934b504d3ba15ca31e86294f0b01fd201a0cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300c0c0c0f84894d5055a782813da3862e9c9980bb047511960749fc0c0e8d3818a900000000000000000000050136f3ce511d3818b900000000000000000000001e8f1c10800c6c5818b821905c0f494d519cad6fbdb66ddb282d182d2c816c7bab07601c0c0d5d482013790000000000000000001bb7e1e1b4da3bbc5c48201376ec0ee94d5ab48f40bd9fe23c91c841d1285c249a4bf1bacc0c0d4d381f390000000000000000014d05c275a218000c0c0f294d609485c109e3fb9721b605d99c44e7c0c6b7d98c0c0d4d381e090000000000000000003103352214c1d59c4c381e03ac0f094d64fea829f2b117cc86be5f84697337449eb0a20c0c0d3d2729000000000000000000010b5914f45c26bc3c2720cc0f294d6e86437cdeed131f5ae496a81ebfc8ad32f8404c0c0d4d3819b90000000000000000000000fc33e39ce4cc4c3819b02c0f83b94d703aae79538628d27099b8c4f621be4ccd142d5c0e1a0c1c54f32065b0650d9213ccb9e868d7b2b3e86d37ccbc99e0a93122ed102f1f2c0c0c0ed94d7463d3df4081d751b26329ecad08ab5650821d5c0c0d3d2629000000000000000000046acab46fb3800c0c0f89f94d75a60afcbc113c1c76e42184663da141f839053c0f884a00000000000000000000000000000000000000000000000000000000000000007a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c9b7a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f094d773f422d9e505d05de084b36a07075b75209d7cc0c0d3d2169000000000000000000017a8e48b32cfbdc3c21601c0f9017594d774557b647330c91bf44cfeab205095f7e6c367f8d5f845a071dbd51ab73c2c78feef3bec8132148c156007f5ac50b5bd880f026a67d38d74e3e20da00000000000000000000000000000000000000000000000000000000000000001f845a08515324f70593fe473ac361da0f982276a44dcfb1777f82d75ef40d4b2839f61e3e20da00000000000000000000000000000000000000000000000000000000000000000f845a0b029eafeb51e5998dc0654ec19c40cf4043037bb24f6604f0e8e52fb667fa4e2e3e20da00000000200000000688a1197eefc04cdac13c49ced4ec03be7345b9a55555e2ff884a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000016a0087f745496bb462f7aaeebd5723ac6cbeab25cb4762f8980754b216072110ddba07ed53fac0420d4d8bcdeda0f96369166adb26777a0979e70920749d5c105d072c0c0c0f094d85ed0e5b0130e1f9b9f7ae59a1c73387bde4651c0c0d3d23c9000000000000000000004eb87fa5c6881c3c23c01c0f294d88e6bf720452c3fdf92dd89ff55d8136021e71bc0c0d4d3819a900000000000000000000131c2ae2a225ac4c3819a02c0f694d952f539b8612694378e9298a1aba62036c010e3c0c0d5d482011e9000000000000000001a41e270ccea44bec7c682011e820119c0f494d9c2170878db71fcdf4ea2c7cd1d606475cc384ac0c0d5d482010190000000000000000000049d3543afe84fc5c482010119c0f294d9e0de3a144db007d0cea41905018ebe84a19a48c0c0d4d381a9900000000000000000000033d758c09000c4c381a901c0ed94da6e0384c4ba56ceecb64f625acf1fd4214ac645c0c0d3d23b90000000000000000000073ac27a9de59dc0c0f294da941d5041636b0d45a372d95cecb46deed26573c0c0d4d38194900000000000000000000138207ead925ac4c3819402c0f9150694dac17f958d2ee523a2206206994597c13d831ec7f90fc0f845a0010ee45acead1949877eba539d021f02e78c799e290ff49cabf14eccbbc80dc8e3e231a00000000000000000000000000000000000000000000000000000000e6624b6cdf846a0012e2698f7c0296097db4431e04ec2bc8f617063fec7dc24b54bc9a6ef792e69e4e381dca00000000000000000000000000000000000000000000000000000000000000000f846a006662ee7694feab155ac1403967ed86459f3ae1f78b36f3ca5b990c98639e521e4e381a3a00000000000000000000000000000000000000000000000000000000000008bd4f845a0091db9d9a9c6e118727ad07f6cd0cfe3b3277fe2b643c6b0eff07704f324c69ce3e231a000000000000000000000000000000000000000000000000000011d6d8d1dbb37f845a0098542e5dca0f570929f6e8a011795ad3c1017c2e6cc71e0357aefaa2827ed81e3e263a00000000000000000000000000000000000000000000000000000000000000000f845a00bb53dc5636274aaa5c5f6b30de6383ed141834d816f70940a3a95f81a84ffd3e3e233a00000000000000000000000000000000000000000000000000000000000000000f846a016453497ac0bdcc383ab404efc0c22904d0f8eb7246a1f4a0e894eec2884335ae4e381cea0000000000000000000000000000000000000000000000000000000000c769ce0f847a0169228ca33ea854d54aa1e506e59ec687f618a41074f5f5de937a0e9c6343e5ae5e482013ca000000000000000000000000000000000000000000000000000000b8ce04cc409f847a019853c897618c9c0b744a559e6fa594161c914c8420240a87d50a95be8ee6553e5e4820122a0000000000000000000000000000000000000000000000000000000000da29de7f847a01e94d915672230ecb7d63beff03cc51238d99e81931e330cd7083b8d0d745202e5e4820122a00000000000000000000000000000000000000000000000000000012268a6f195f846a0205f3a071cc7f68d7ca7bab24ed6f78a0e2ba9377d5cc04343fa91fe6ea960c8e4e381b2a00000000000000000000000000000000000000000000000000000000922a7e358f847a021386081866b0f509bc0fe2dcbac3f64ced018a940b764f8d81d99ae5580f907e5e482013ea0000000000000000000000000000000000000000000000000000000000006f198f846a0326695ca13563327c27f892d50db8b7940486eaba321f5d2ae15606c636a3d3fe4e381d3a0000000000000000000000000000000000000000000000000000000000e327db0f86ba035d7fb7665514f774d2c2df607e197eb8674b6e63d2638472758647a2e67406af848e222a00000000000000000000000000000000000000000000000000000041492d78b72e482013ca00000000000000000000000000000000000000000000000000000041a5322a0c0f845a03ae4a71841db7a729399381506b05d53bdc2176f18642012771fd15317f1b20de3e21fa00000000000000000000000000000000000000000000000000000000006350ce0f846a03ee1bd6d278517ad2a4f241292ca82943d4d026e8794f828f06af8cb9d3ad594e4e381fda00000000000000000000000000000000000000000000000000000000005d0f3e0f846a04166fd35951e38bb7049abf2664fb4ab3f47f65fca54e107e7976e2cc1a65447e4e381a7a000000000000000000000000000000000000000000000000000000001889e7dc0f846a041e9f97bfce85596afc82230caa082dc49bcd13952da65f4481bc29e8a29549ae4e381d8a0000000000000000000000000000000000000000000000000000000966f2e1c0af846a04966c331fd21bb95e5096f54ff5bc865ffddab6a0f3c4edcb615c80b8d472ba5e4e381d8a000000000000000000000000000000000000000000000000000006696c09be90ef846a04d05df8ee08876e0beecf6bf7f10f3f49de471880214be117126cbfc989ba055e4e381bba00000000000000000000000000000000000000000000000000000000000000000f846a0507800023e98b83ddb7530e2ca770c8b7b257ee4814ae4d656f41e451029274ae4e381b2a00000000000000000000000000000000000000000000000000000002eed13e5c7f846a05e797cce47bcd782ced8fd290c03ca6235fd69dc45367ed35cffacac1d7bbffbe4e381a5a000000000000000000000000000000000000000000000000000000001122e6e00f88da065e825459894c28caa3bf45c046bcb806478983a744f47c04782164e994daae2f86ae21ea00000000000000000000000000000000000000000000000000000b2bb5aa4b5a1e21fa00000000000000000000000000000000000000000000000000000b2bb57a7a241e381bba00000000000000000000000000000000000000000000000000000b2bb8d2b1ab3f846a06f5981580c988edb58080dfdb48d7c0c4d2c84e66babd1859ed594936d9a6cdce4e38181a000000000000000000000000000000000000000000000000000000000074a2660f846a0702d961875f786cb53ba76bd893cd31e95d09cd1caab6f45f8a5b9a0fca3c131e4e381f4a00000000000000000000000000000000000000000000000000000000015ef3c00f846a075163de790a78cc82f42f45d14b90b6ef3ea879a2c78dfec960d143b0272441ce4e38185a000000000000000000000000000000000000000000000000000000000c532860cf846a078b35599871be95768b2fdfaf9293a4491ecdc8ef25b872ee404fa1e441436e0e4e381b6a0000000000000000000000000000000000000000000000000000363c3840c574df846a07dc909bd982997536f175846bfe9d231280ca5132f05011845e67862a8ada194e4e38181a00000000000000000000000000000000000000000000000000000000000000000f846a07e6dc8ad1383fb566081d3e4df148d026e5118386ed3ade7d2eded6ffc3a917de4e381d6a00000000000000000000000000000000000000000000000000000000008e95811f846a08300cc53f61a4c7b23603082c3b6f30c9e609cf582d9371dfc97f1a84416fca9e4e381a5a00000000000000000000000000000000000000000000000000000000db75da653f846a085456519d71699f68c07e97ca0e826687d8ac34844ae6047518b5c19d6acd032e4e381dca00000000000000000000000000000000000000000000000000000004e79fcdc68f846a08c5fad4f7ce194285eb0f9b7c7a7c8742b655fb32af9d297b1b81f559efd89b4e4e38185a0000000000000000000000000000000000000000000000000000000003b95f0b1f847a08d55779954f3aeb0eb7fa7a0f55012a69cfd62879b9e3628d84f73f93afb059ae5e4820135a0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845a090f02405e941d2e0589e86ae70c028f367128bdb627bfc5380c851a078b4460ce3e232a00000000000000000000000000000000000000000000000000000000005f5e100f846a091f0e4f6e05dbbb514bc4cf940cca183556079b5e5807deaea0f5cfe137e71f6e4e381a3a000000000000000000000000000000000000000000000000000000011985cbed8f847a09417a648468c3cac073b004e2e206a28a66554481070d068ab6c33ccf2e2acc9e5e4820124a000000000000000000000000000000000000000000000000000000008c8a1d187f847a094cd747d0493e4c3e2a9093b7045c88cc41d729edc4afa05e4063b39502084abe5e4820103a00000000000000000000000000000000000000000000000000000000023c34600f86ba0a3b88bef8df2f6096822c988ffd14e98b0513d518130e72efa1debea259cbf43f848e381d3a000000000000000000000000000000000000000000000000000008376e408d302e381d6a000000000000000000000000000000000000000000000000000008376db1f7af1f846a0a4c2b4b10c784ac08d6e785a62c7805ab5b11cf48dffc50561d457a62988e50de4e381a7a00000000000000000000000000000000000000000000000000000000403fe8046f845a0abdf55938d7687a176c3b405c64b0999584cfa1076137127b94f01ac164d48dae3e252a0000000000000000000000000000000000000000000000000000000014a873233f846a0aeaaef6bf10eec9d792ab619d7408738d380fa1a2a3680f4c9d150e6a2274f4ee4e381b1a00000000000000000000000000000000000000000000000000000000000989680f845a0aeed24d3ec3e9b34d78813777ab6cea0c58c58eaa158a04e69dbfa60b4741e25e3e263a00000000000000000000000000000000000000000000000000000000059682f00f845a0b4e1f9d6d9eb60dd6385737433550a0c037867d7a9b23e9958e17f494701c3ece3e252a00000000000000000000000000000000000000000000000000000000000000000f846a0c1b90b187e69c17dfe5dc7c7d6ee1cd03e0a45bd6cb877905c093f403f880bc6e4e381b6a0000000000000000000000000000000000000000000000000000000000b9f76c0f846a0ce14656036e17a87cea5093cc525ed309b0a84b2a8e7933d20e718963514a00be4e381b1a0000000000000000000000000000000000000000000000000000000018c0a0004f86aa0d06ef82a07326a6e964ce1700825547c428fbc96deb2e33e7e20498001080d7df847e222a000000000000000000000000000000000000000000000000000000570ebf25b13e38185a000000000000000000000000000000000000000000000000000000570aff0e2a1f845a0d550f0f4b2167d6425eda4d01862155673532665dc1b18f3bf88e2810adef465e3e232a00000000000000000000000000000000000000000000000000000016ef922b77cf846a0df70affd7af96966d2670c0f3ed33a9da6b177986dc23d985c9fbdd265f954a7e4e381cea000000000000000000000000000000000000000000000000000005e96593dfb12f847a0e9561e474d18eee2e0b1eadbbf02bc673103c4f888c69050023dc41f7eab4e47e5e4820124a00000000000000000000000000000000000000000000000000000000000000000f845a0ec455b5dfb0a23ba20e161abdbe1ac6daca7a5bed55a5172df14e281f6576e02e3e21ea000000000000000000000000000000000000000000000000000000000022f0214f847a0f2b603b8b87884f80a87138a09cd511bfd30ff9c70d4454c852060eab7d35cc1e5e4820103a0000000000000000000000000000000000000000000000000000000002b4dc48cf86ba0f94b05ba7d0a6aa6692eecf3cf30c6ca5d527c3e5fd03d7081ff14e4910ec033f848e233a00000000000000000000000000000000000000000000000000001187e34fc97c7e482013ea00000000000000000000000000000000000000000000000000001187e419896f7f846a0ff2acf1040b8375aae9ffa186abdb003c9aec45e162f5b564ab4bc02ff148678e4e381f4a0000000000000000000000000000000000000000000000000000041d189bdc4ccf90528a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000000aa00a2899f983353d31758d7dc1b242eafdfd6645fea4842fbab8a68ad7e79074ffa0125617aba6520df91ba2f6482cb0f4289d91d40d63d800e4260764bc629f6b28a022fddf1564c6df0782dd8fdb3d9c2acb66635cf7ce8550a7e275f8a61aebbc48a02a63c13458ad66368443e3d2e2647e67d325cffe9a3e00131b204db545d6daf5a02d1b4c814fbdaef8c9507ba0630d2dad0af80fd2f182cf981c7634bb9c96ec75a02fa180f7ea52504da27316a4288fd6a940ccf7214a708ca5c4371e06aa8d560ba032bfdf0ea3a8758e06fb5062d44430296208c490a4ef6f74ef7daab46a9daa33a03ad2db55fe5657fe773e3b7111e43f4b662a181a20e875b3b8be52dd9f0e2333a03e1b68b6d651153b7a461a5b6ae597ec232abbb94ddacdfdc6e2c1516897ca62a0447f801ecd7e6ab47cbe7ad0ce36e9df21f75f7c2044ee5318b3647aceb856eba04f423963bb26e5e82fc5f4d134e79edafa336aab3357b18cf082b215262a8331a0513e3e116209adb6412b240bd8470864f36bb58bbd57ffae28c277b2fb1996a1a0543e9815de75f84feb85b9a4bfc718ebb17c5fc8fbcd4593544ed6b8ad77e291a057a88ac119e9fd51495848929b3bd1b2afa77fa8ccc7197b0769bec85463aaa6a0759ebf061baf585c99e31ae2e878792704a737b70719d1ed8895fe7d10c01bf7a076c3196c40c1047b842470c952d43291e241a446825b4c8ce4a16add3def3f38a07a464961c125fc990109c53fc6324f805cc788adf74934778792f1f7cfd292baa08d2f8ed385a5e54ce1fb7697f6dc700f7b39fc435bde364c6104b657a1b25874a08fe6c2b94cfeb65caef5a01fd4f3f9e4001d9f1261cad7bc858be81b37a2bb70a0911b0cefa008e074d4280934de50b4b1b8f05de617405d8de4c6ed613956722ea09598b4e483d0cabe0a56d7eaa8d8a372e6b1ea15e197d2a68207d90e2240f04ea09666d24b0b766b5ea0b25f70d2919907474d959623cf4270f64590edf7474894a09c896fe541acf9f15ae430cc488a39173f95ac859a96fcfd6aa32818072410f0a0a747ca6bc08aa9bebf2827869a8f6f5a67ddaec42654c8a09a958368cedf919fa0aaca8adeba41402e798d23c6fa1e4c59bebc1b015479e88abc524873577606b7a0beaced3d2b647528b23073319ec0275def0dbad9c424c3f35b17e6024731a60ba0cbb01fc6425c1a34b1f955b23ec29481607a8b65109024ad87949f3acb43077aa0d1eef4f4158ca69b8eeec0f5e41d3a88b8f58a6a1cb0ebd1a64a77d20eb5e508a0d297114012a56198e4078fbbc01248b079e0d51b2f5dc2ca3c038c5d19170832a0d2f26d0f1caf1970b353c424c3383553e2250f3adaac73ea5a85bb766824d6e5a0d8e9d8ea6f134d03da245979710275316370450c0e8a5afcfc4e8e535674cd77a0ea66b23739c88c4f9d97acf50a97d8c337fa4f2ab587d059ee953d0cda66978ba0efef8b0f5a82fde715bb2988d0cb6b4085bf6a1886304f75dd2be5ca27f23529a0f7d9cb707f6bcedae1da42a45421a9c7cc1aa05971d3473ad51b9515039017fea0f991184810abaacd6ee89d436306af648330a6b462c169e9594a6e9a392e9fc4a0fab60c9cffe3fcdc6b3fe890e5e7f90611ce0db7e7302ca8dcce864818bd4d51c0c0c0ef94db63440638307c8d80b638426eed8df6cc27a210c0c0d5d482010a9000000000000000000f03412db11f4000c0c0f9032a94dc035d45d973e3ec169d2276ddab16f1e407384ff902aaf845a00000000000000000000000000000000000000000000000000000000000000001e3e208a000000000000000000000000000000000000000000f08c4108c8269b504ff946ff845a031adef62206227419133dd9a6b4041532c22595206a596cf74f19493bfc8f368e3e208a0000000000000000000000000000000000000000000000009d18dab239dd07072f86ba0504a62fae10df45f50376ec7922e386a7acac2c4b795f91a639350bbc3d00c02f848e204a00000000000000000000000000000000000000000001122d89332dbc79b0f6875e482010da00000000000000000000000000000000000000000001122d97ee29c50d0c7c89ef845a08d756b0c25da8ff78cb57dc7f0a84e0129be053d02c43098e1e25e3e9e2bdf73e3e208a0000000000000000000000000000000000000000000003fed7d01c7310c03be34f845a09201bf90a4e7720a6332e6fc45789e7efe49a64f51b014e374446558ec26526be3e204a00000000000000000000000000000000000000000000000996a23b60473780000f845a095bf9ded295729c1f99631fe3757452f817fbca3d1318ec783e1a9f215498212e3e237a00000000000000000000000000000000000000000000001adae3b3b71d8d00000f847a0b475e89f4461c0c02a45185b854f9a1ed04bace2c9a69f8c64679970b5791ab9e5e482010da00000000000000000000000000000000000000000000000000298577e53bc9fd7f847a0ca89db3ac11bbc9e449a5d63fe9e404d168d96d38850e71570dbf9349d6b43c2e5e482010da00000000000000000000000000000000000000000000000004c4cb1f8fa3e5f28f846a0e542bb1f99cb2a44bc925b4bfc0ae5488e088171da1df43f473ca7e6c0e907ece4e381e6a000000000000000000000000000000000000000000000005fd2fc334d78240000f863a004af2650cdc358b537adffd5f648a345270bd1eff29d11e7735cd7b84ef65741a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0c1c4ba3e29f945ec5afe27ef72b2e9908b20c1f712a4a8aa71a582b97b3a0789c0c0c0f394dc7bf4d19f98e3082251a75938ce4775224b19bbc0c0d4d381b390000000000000000002086101154a2221c5c481b381d6c0f86394dd3b11ef34cd511a2da159034a05fcb94d806686f848f846a0ddeafbfbd84bd37a420faa42e95f479566d43aa1dc6c007f7909133572b74234e4e381eda0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0c0c0c0f594dd3d72c53ff982ff59853da71158bf1538b3ceeec0c0d4d381829000000000000000009079aeea0ade3ebdc7c681828308153ac0ee94ddfc5a330f44463d973cb8f914bcfd1804e270e3c0c0d4d381ec90000000000000000000016bcc41e90000c0c0f694de303e9d6b88ebe371cebf9a519fa4dc48089a85c0c0d5d482012f900000000000000000001197feb2285099c7c682012f8203cfc0f88394de632c3a214d5f14c1d8ddf0b92f8bcd188fee45f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e208a0000000000000000000070000f0d114232f9d971e4e759cf3c352759cf4a63e74e1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0ed94de7259893af7cdbc9fd806c6ba61d22d581d5667c0c0d3d25e90000000000000000000337f76fed71e24c0c0ee94de7562cdb6e53fb9dd1e39c8407ecaeab5b51fffc0c0d4d381da900000000000000000058b71811bc86678c0c0f494ded4e991819b9e66ad5302fe3f1932461e69ceddc0c0d4d381a3900000000000000000000bff4b2dcbd3b8c6c581a382015fc0f9028494def1fce2df6270fdf7e1214343bebbab8583d43df9011cf845a00000000000000000000000000000000000000000000000000000000000000002e3e204a000000000000000000000000000000000000000000037b4a1998cea8c50a767d8f845a00000000000000000000000000000000000000000000000000000000000000016e3e204a00000000000000000000000000000000000000000003983c2a9889fdbbaab586cf845a03773aa40f235bd6d49b3c5ff1680c632908957bd1dbb8aa897a3dbba9148e4dfe3e204a00000000000000000000000000000000000000000000000b76b9b90ad95fbe893f845a0a103e3ca16ac9d936492ba26988f3c70ff13ddc992f8ebc1a189fd16ed521a9ae3e204a00000000000000000000000000000000000000000000000949160848abf9e4446f9014aa00000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000015a00000000000000000000000000000000000000000000000000000000000000017a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec475a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec476a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec477a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec478a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec479a055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec47aa055f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec47bc0c0c0da94df31a70a21a1931e02033dbba7deace6c45cfd0fc0c0c0c0c0f794dfaa75323fb721e5f29d43859390f62cc4b600b8c0c0d5d482012a900000000000000022be878d073838e9bbc8c782012a8304aa4ec0f8a294dfd5293d8e347dfe59e90efd55b2956a1343963dc0c0f864d381d39000000000000005b6cce2695e97eb2700d381d49000000000000005b6cc82ff24f6f0b9a8d381d59000000000000005b6cc8265aad106a8ecd381d69000000000000005b6cc81c9d9c97edd09d381d79000000000000005b6cc81306736370751e3c681d383b7c89dc681d483b7c89ec681d583b7c89fc681d683b7c8a0c681d783b7c8a1c0f9015494e0554a476a092703abdb3ef35c80e0d76d32939ff8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e224a000010000330033000002f5b10000000000003f9c739b2ff7c4bc2d1c6276a133f845a00000000000000000000000000000000000000000000000000000000000000002e3e224a0000000000000000000000000000012e8262664e27aa91412d1c5ff02a109fc66f845a00000000000000000000000000000000000000000000000000000000000000008e3e224a001002a700a000033873a4a305266ac5424eee1c50700152f3d302280688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000003aa0219bb19de15289a5105980da3f53414be34e28ab30c2a2f67932fcfb522122dcc0c0c0f9015a94e092769bc1fa5262d4f48353f90890dcc339bf80f8dbf847a00000000000000000000000000000000000000000000000000000000000000000e5e4820141a0000100004000400023ff7a1800000000000000002e1f5565d7a99913009b4152f847a00000000000000000000000000000000000000000000000000000000000000001e5e4820141a00000000000000000000000000000000475f7a5b3622044481b93387a4745b816f847a0000000000000000000000000000000000000000000000000000000000000002be5e4820141a001000000000000000000090768016a3eb158249b59fffe0c48924698688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000002aa063187d71e139eee983a88d0737447c7451979b3dbb75903c76b5fe430d36588ec0c0c0f85d94e0cf093ce6649ef94fe46726745346afc25214d8c0f842a06a050807f83537a6fb37f3bbb52aec8cf8b181c24d1ee209b83e85d563959f94a0b701f162d2f11e4238517eb7c8562a022035aea7e623e23c45d3c52011bedc21c0c0c0f9028494e0f63a424a4439cbe457d80e4f4b51ad25b2c56cf8dbf847a03bba9108b904cfb969b317a6a0847c2d13a92bb924e87f843935fe7b8f315911e5e4820140a000000000000000000000000000000000000000000000000000000009dcca783cf847a09d8106a7b639091777047eab6393a843ae4f778b78087f1dc0a2a206ede77128e5e4820140a000000000000000000000000000000000000000000000000000003f96d72dcb22f847a0c0bb2d864141e3dcf7dad97a717c50fb6136318d913ffa59cb4f47ed4f0c1898e5e4820140a00000000000000000000000000000000000000000000000000000002432e46e5ff9018ca00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000014a011234190717ef1d3c4e2cfd4321470b7159a7441273729cfb45569397b76f4efa05c112e25c8ca97e0ac5683e85b3a24d8fba09abed7a3ac95561d1e3222ed5324a0ac6fb4f0f81e11dc1f89f596e243b0ed419667343ccffe17523030f10980cedda0bbdb6c46a65073920ce1493e5770cf927f61df994d2df4d42ef47a5eb0452f16a0c02c10ae588f2466ea6e6350d07ab12ee7521cf6f0c110cec9b4bd1ec5838085a0dcfe32d795676ae47de7ed447c658398dd1361446ecb596c0337c8fe9e7951ebc0c0c0f87e94e13fafe4fb769e0f4a1cb69d35d21ef99188eff7c0f863a0000000000000000000000000000000000000000000000000000000000000000da00000000000000000000000000000000000000000000000000000000000000012a0e464df08b1a6bc4a7efd0b57900b5ac59bc86655560732a1d4391a671c7e1d1ac0c0c0f9015794e1a4506697e653565b80cbb773760f8067e34ea3f8d8f846a00000000000000000000000000000000000000000000000000000000000000008e4e38184a0688a119700000000000064263e48896de706000000000dc921fa94b282387beaf846a00000000000000000000000000000000000000000000000000000000000000009e4e38184a000000000000000000000000000000000004e1c74d37b72056e36919970027f50f846a0000000000000000000000000000000000000000000000000000000000000000ae4e38184a0000000000000000000000000000083195c77d604047fd4b8583ec985e03be744f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0f494e243b5b9745300354d447e9fc57d9d931b86efcfc0c0d4d381a4900000000000000000036184f09d729764c6c581a4821ed6c0ed94e3478b0bb1a5084567c319096437924948be1964c0c0d3d2159000000000000000268754b4f10bb1d84fc0c0f85d94e3b83f79fbf01b25659f8a814945ab82186a8ad0c0f842a00000000000000000000000000000000000000000000000000000000000000000a0d31f4fef635961ffe318d8a370546958486ab4520b40eac96ae24caff178bdbcc0c0c0f294e4166f06730fe19e6487a56ba79b2371b4c52515c0c0d3d2039000000000000000000022aeb1846a4157c5c4038203c1c0f83b94e49210ad0684dfa3690734e239ac816bf483b021c0e1a00000000000000000000000000000000000000000000000000000000000000000c0c0c0f294e4c72b5f538d8619f2c459bd3c706e791145a93bc0c0d3d25d90000000000000000001ec6e6837ace33bc5c45d8203d9c0f494e5414ccba3b083b56930d7037625fa734568da1ac0c0d4d381e990000000000000000000984db3050d9bb8c6c581e9820333c0da94e592427a0aece92de3edee1f18e0157c05861564c0c0c0c0c0f83b94e59dbf08cccf8d1ab90156b9664d31fd20bb2ac7c0e1a00000000000000000000000000000000000000000000000000000000000000002c0c0c0f294e620a6b35852c88578d9e611a3bd1d14d2694a9ec0c0d4d3819f9000000000000000000029ae2e03b7731ac4c3819f24c0f494e680366019c64871ff35a6996296782e5a25920bc0c0d5d482014490000000000000000000074516d2822081c5c482014405c0f094e68df48c92b21367043797c24f62c392c5d922eec0c0d3d24e900000000000000000041226775a62e267c3c24e2bc0f9015394e7351fd770a37282b91d153ee690b63579d6dd7ff892f847a0efcb751bd2e77b8fd7a2dde57afdcd3ef309ad50df373786465e397276d8874ae5e4820142a00000000000000000000000555ce236c0220695b68341bc48c68d52210cc35b01f847a0efcb751bd2e77b8fd7a2dde57afdcd3ef309ad50df373786465e397276d8874be5e4820142a00000000000000000000000000000000000000000000000000000000000000038f8a5a00000000000000000000000000000000000000000000000000000000000000097a000000000000000000000000000000000000000000000000000000000000000cba0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0bc7d7a646c08b125fcff2052fb7b7c18018756a26f7c9cb7febc577fc6dbbed5c0c0c0f9015494e936f0073549ad8b1fa53583600d629ba9375161f8d5f845a00000000000000000000000000000000000000000000000000000000000000000e3e201a0000100009000900057fef16f00000000000000000805834b02ee9d244821cdabf845a00000000000000000000000000000000000000000000000000000000000000002e3e201a00000000000000000000000000000000008a6f21839d6162ba005716279a2c995f845a0000000000000000000000000000000000000000000000000000000000000005fe3e201a001009f41af00000000000a80beb2d6656d0ecaff73fff10682cad28d688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000005ea09c04773acff4c5c42718bd0120c72761f458e43068a3961eb935577d1ed4effbc0c0c0f9015a94e9c761b372a9e98c966baac334a05fc8c12771b3f8dbf847a00000000000000000000000000000000000000000000000000000000000000000e5e482013aa0000100007800780040fe7b1c000000000000000001c39fcf3a5a31906b087ef7f847a00000000000000000000000000000000000000000000000000000000000000001e5e482013aa00000000000000000000000000000000f26f9484874dbb131523596db116de20ef847a00000000000000000000000000000000000000000000000000000000000000048e5e482013aa0010370fbf0000000000672b98346ea4509ee4b13b2fff51fbbb8986c688a1197f863a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000047a08a9a513b9791a18d5272680de0b8170980d584db74d5cc608d89e97f4ae4c892c0c0c0ef94ea1efc089232816a401a20e85ddab94d3b8264a4c0c0d5d482013190000000000000000000055487da27bf18c0c0f85d94ea674bbc33ae708bc9eb4ba348b04e4eb55b496bc0f842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005c0c0c0f84294eba88149813bec1cccccfdb0dacefaaa5de94cb1c0c0e8d381a890000000000000000212a79f7db44ceb9ad381a9900000000000000002239e639fe4d7ab9ac0c0ed94ebc6a1749f2c536c161b6d5ad88276e7a27556cfc0c0d3d20690000000000000000002d31d23291f7387c0c0f194ec1e2f0fb0a2147e04aad5ad9e8e86fcff13a144c0c0d3d20a900000000000000000005ecc46b9b2e6aec4c30a81fcc0f294ec69aa3aafb9187a466f0ce0f75a8bb7e6786a9fc0c0d4d381f9900000000000000000015f64eba76d0ff5c4c381f914c0f9015494eca3607e271b53ec39eb3a8f70a0f697c0551ee9f8d5f845a00000000000000000000000000000000000000000000000000000000000000008e3e214a0688a1197000000000001569964a2a306a76800000000000000b3ef4a469f39c9f845a00000000000000000000000000000000000000000000000000000000000000009e3e214a00000000000000000000000000000191c676f69026e369f826b1d7ac6b2dd0c04f845a0000000000000000000000000000000000000000000000000000000000000000ae3e214a0000000000000000000000000000000002d8cdd05a0bdaed885fc7648be5c53b8f863a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a0000000000000000000000000000000000000000000000000000000000000000cc0c0c0ed94ed310c9d7d88828f4c66735ec48fb932d7cf2bb9c0c0d3d26c900000000000000000000d29ad88e49000c0c0ef94edbbbd423691e6d3011baebd58e9a9234a2dd469c0c0d5d4820142900000000000000000008c507cc64e178dc0c0f694edd1ae45b7727fe87bb924abbcbf7a662475f17dc0c0d5d482012b900000000000000001379864a46f17beffc7c682012b8266fbc0ed94edf48a5981da93ff92c0cded4d2a892b72ddcc01c0c0d3d2539000000000000000000074c6127c90a004c0c0da94edf63cce4ba70cbe74064b7687882e71ebb0e988c0c0c0c0c0f9014594ee030ec6f4307411607e55acd08e628ae6655b86c0f90129a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000008a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba0000000000000000000000000000000000000000000000000000000000000000ca00147d6e0a075db7ac4e79e624ee65862ab0638d1f09c1e8bb6764e99aaeaf702c0c0c0f494ee128271d882ccc1a14ca82dc3bde5ea369165f4c0c0d5d482013890000000000000000000003520af1f8fb0c5c482013810c0f294eefc04cdac13c49ced4ec03be7345b9a55555e2fc0c0d3d20d9000000000000000000650903e44e98f81c5c40d820854c0f494ef74e87cedb44c84095c1d5f5198e657756f51dac0c0d4d381e6900000000000000000005e2f762983f4d3c6c581e6820129c0da94efc2c1444ebcc4db75e7613d20c6a62ff67a167cc0c0c0c0c0f094f093f201d85872748a3a1f8f09808e713cd9c0f9c0c0d3d246900000000000000000000784295118ffd1c3c24610c0ee94f0a86031c0a007c4628f47d0e8af0eeb3c38b68bc0c0d4d381b8900000000000000000000d0e9d375aa1fec0c0ee94f0b0816e0be8c3db2dc0c8cc3fb1de1ff71a7e0dc0c0d4d381db90000000000000000003a51a9940defcddc0c0f903a094f0bae7189bf0ebbaa76b5a6b372c9db837e455c4f90238f845a00000000000000000000000000000000000000000000000000000000000000068e3e221a0000000000000000000000000000000000000000006b5eb656fe3203cf23afdecf845a00000000000000000000000000000000000000000000000000000000000000069e3e221a000000000000000000000000000000000000000000000000000000000015f70a9f845a0000000000000000000000000000000000000000000000000000000000000006ae3e221a00000000000000000000000000000000000000000000000000001e6d437788a01f845a0fb99638a8a0d4f3d05233afb0414573fd446a47342aa07bdec575bfeb3883072e3e221a00000000000000000000000000000000000000000000000000000000000000000f845a0fb99638a8a0d4f3d05233afb0414573fd446a47342aa07bdec575bfeb3883073e3e221a00000000000000000000000000000000000000000000000000000000000000000f845a0fb99638a8a0d4f3d05233afb0414573fd446a47342aa07bdec575bfeb3883074e3e221a00000000000000000000000000000000000000000000000000000000000000000f845a0fb99638a8a0d4f3d05233afb0414573fd446a47342aa07bdec575bfeb3883075e3e221a00000000000000000000000000000000000000000000000000000000000000000f845a0fb99638a8a0d4f3d05233afb0414573fd446a47342aa07bdec575bfeb3883076e3e221a00000000000000000000000000000000000000000000000000000000000000000f9014aa00000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000006ba0000000000000000000000000000000000000000000000000000000000000006ca0000000000000000000000000000000000000000000000000000000000000006da0000000000000000000000000000000000000000000000000000000000000006ea0000000000000000000000000000000000000000000000000000000000000006fa0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca08093b32f3d03a295948279e7b7486c594a9e251ed35fc1543709a9a77eb3b1aba0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0d0ad710dabeb0bf9758fa9998a9d0b9ad80960ee51348684b4be199ceb27f8c1c0c0c0ef94f2f6531f66e22d51b6dc978348a810050c3ff48fc0c0d5d4820130900000000000000000001094238ac06000c0c0ee94f2f77cb086da7af5205bb137e5f0eb273ac658aec0c0d4d3818f900000000000000000009a23d5748fc3f3c0c0f294f3a32ac94a64ff42edcfbb692fd151babe518057c0c0d3d205900000000000000000086c87525ea19cddc5c405820134c0f294f47b274bf5e6ac9190620bca1ef10aa417d00319c0c0d3d21a900000000000000000006c0b80de5381e6c5c41a8205eec0f85d94f5042e6ffac5a625d4e7848e0b01373d8eb9e222c0f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002c0c0c0ef94f52605c7b778563a5a9144ef4dc53b57463ca2c7c0c0d5d4820113900000000000000001223ddc0dd06c6c35c0c0f094f573fba8aca8b861731249f6ab8a595f8940c9e7c0c0d3d2219000000000000000000021a5161d2cceb7c3c2212ec0da94f62304dbb23275ae07c6b5af8c7364cf3784ba7dc0c0c0c0c0f83b94f6473fa77d6c2b7e4c32936e7bed7b213aa4325ec0c0c0c5c482011e01dcdb82011e97ef0100411d38d27f6f2c7f3b70ff29dada64cbd7bfa9b2f794f6d4e5a7c5215f91f59a95065190cca24bf64554c0c0d5d48201309000000000000000060f41a9582694f0edc8c7820130830102fdc0f83b94f6e72db5454dd049d0788e411b06cfaf16853042c0e1a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f88f94f70da97812cb96acdf810712aa562db8dfa3dbefc0c0f862d2069000000000000000216eaab20c6006b7dcd2119000000000000000216e9c7cdd70f5d9dcd2129000000000000000216e9a37668591c25bd381909000000000000000216e9dce0aa4e0b631d482010b9000000000000000216ea0a351a351ae98d2c506832eb16fc511832eb170c512832eb171c0f494f724ecbf1c623691b7926452d452a5cfb5c15752c0c0d5d48201179000000000000000000022348da7fa935ec5c482011709c0f394f746d71c795ad20787c061c3d9b20e6b1d6a325ac0c0d4d381ec9000000000000000000074ea2194eba7c3c5c481ec81b9c0f294f7c97cdd83b17fb16cb8fc739f1f738d05bf18f9c0c0d4d381ac900000000000000000022decf9e5bcce1bc4c381ac49c0f294f866bc6f3622b687873a6b38b4d5cc35dd7f14bcc0c0d4d3819c900000000000000000008f91b595069f7ec4c3819c03c0ed94f86c6a05a5184592556074cc549013e56ab931a7c0c0d3d255900000000000000000000c9609d7af9f2cc0c0f9014b94f8ab2dbe6c43bf1a856471182290f91d621ba76df848f846a0bff8e45742cd98c178e13ccb0a80ba8aa27033ea4b8ce6352653517fda8d6850e4e38190a00000000000000000000000000000000000000000000000000000000000000001f8e7a00000000000000000000000000000000000000000000000929eee149b4bd21268a001507f32017765a5cdb7ee1b0de7932806231a22d521c294381df8c987762902a001507f32017765a5cdb7ee1b0de7932806231a22d521c294381df8c987762903a01105e3a5e09fef9802c1673427a4426578a66df072b53d7c2b4374e406740169a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca06b614260192271344ae671278f13c26450d600eb9ccf9b5c9ec5ce3c5b405219a0d40de0af8d9b50cb541ba7e1c8a1245db546361652a5684ca8eab5d31a204826c0c0c0f83b94f93191d350117723dbeda5484a3b0996d285cecfc0e1a00000000000000000000000000000000000000000000000000000000000000001c0c0c0ee94f94efc74958aef588fdb081b0637164b867a8a40c0c0d4d3819890000000000000000000047cc1c731b2d0c0c0f494fab33555c5960a3ef5beda377b3dc43490e5076bc0c0d5d482010b9000000000000000000003bccbbd0dbb7ec5c482010b06c0f9013294faba6f8e4a5e8ab82f62fe7c39859fa577269be3f892f847a037726c8e8c1b8b3301e2cfbadf9e35806a201bd76b13f288729f71ed580af174e5e4820115a000000000000000000000000000000000000000000000001c9491eb27f7f36400f847a0e7bd7062509abed6600ff9a901b83b8255539df85176d54c5fbdf72966ad9bbde5e4820115a0000000000000000000000000000000000000000000035e4172e43c9246cd1f7cf884a00000000000000000000000000000000000000000000000000000000000000003a06fca8a5341b3cd5043dfa93787b46d80bd1c82c5ef2e94684f0ae3344e3751e6a0c88c68aeebda37930d17589b7dc8000f8daf8d376f87915802e39bc158c61671a0d1a527e9ba89a12597c8855c6f733676f5576981597ab846288e60f33e2a33ffc0c0c0f9016694fbd46de044cf34dfd7386c6915cce74ef186c23ec0f9014aa00000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000aa0000000000000000000000000000000000000000000000000000000000000000ba00175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9a0c65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8a0c65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a9c0c0c0ed94fbd4cdb413e45a52e2c8312f670e9ce67e794c37c0c0d3d2019000000000000000017a1d08e2c4d215e8c0c0f904d894fcee41852e5ec9b907714200b2a3c5b7ac29e7aef9030df845a00000000000000000000000000000000000000000000000000000000000000075e3e278a000000000015b6da1000000000000033200000000000003320000000000000331f845a04b987a7c6f512cf0d3515ea389cd88d89be38dbdb4b081db64961c39f48f31f3e3e278a00000000000000001000000000000000000000000000003320000000000000000f845a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad4e3e278a00000000000000001000000000160358900000000016034450000000000000330f845a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad5e3e278a000000000015f6f65000000000000033200000000015f70a90000000000000001f845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e308e3e278a0f8734d40457e9794e88505de53bbbd1a77544f273223652cc1a6efc43051068cf845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e309e3e278a0fc32d4960e75784aa0e04bb437ca94ff4e860b663510e796109379cb541a12e1f845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e30ae3e278a00c352f8c1e9322f6749f36f9a66c7c15122aac8426e575184386bbd86837056df845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e30be3e278a00000000000000001000000000160358900000000016035890000000000000331f845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e30ce3e278a000000000015f70a9000000000000000000000000000000000000000000000000f845a093be49759a4d1a649bc96da5cb9a9ca3ff72cc60898e77cdec8d1d770376e30de3e278a0e165b1b5ba88758e15eb8a173c8fae256b4eef9de7d636e6920bef8a8abb18f7f845a0ad098b06e061271a6abfaa98c13f8d6b2a2553008e1da106dcfb71c3d1be1694e3e278a00000000000000000000000000000000000000000000000000000000000000001f901ada00000000000000000000000000000000000000000000000000000000000000033a00000000000000000000000000000000000000000000000000000000000000065a00000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000006aa00000000000000000000000000000000000000000000000000000000000000073a02b1dbce74324248c222f0ec2d5ed7bd323cfc425b336f0253c5ccfda7265546da032943b47d27a210e8b7e1c927d5bb7b1aed10a78f1f0b3d678c3029f6e5cb75fa063cf6dea2131c66021c98b179b47c74fe3fe9313f961c55303acb46de2ffcca8a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad1a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad2a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad3a08f87f829e1b795a76478796732356cadb5abe698565b66a058a5bec1e6eabad6a0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103c0c0c0f394fd25808ffffbef621c4dbf0171fa647c916cb33bc0c0d4d381bc90000000000000000079a4e0046d95314dc5c481bc81a1c0f85d94fd48112e448417ca79305a518c4186df4b0a200ac0f842a00000000000000000000000000000000000000000000000000000000000000003a0d01a293d013d5426dd5cc2a98a194f6994ad72d277cc3a5942ea20b2a409b465c0c0c0f694fd62020cee216dc543e29752058ee9f60f7d9ff9c0c0d5d482011c90000000000000000002e44040571e0388c7c682011c8262eec0f094fdf6211204deab098aba067dab76d06b40b73edcc0c0d3d2779000000000000000000000000000000000c3c27735c0f494fe263102682933297cb65dc813e5193249769251c0c0d4d381f59000000000000000000d26cea816944531c6c581f582a396c0f394fe93e9ce4f730a1c64846bc782e45904ffaa633ac0c0d4d381c8900000000000000000002280bd5dc61537c5c481c88196c0ef94ff0f50fa7016e4390cb6b069347c635cfd035c6bc0c0d5d4820132900000000000000008afb60eb856ce0603c0c0 diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index 24dfafa0831..515033cafb2 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -19,12 +19,12 @@ package bal import ( "bytes" "cmp" + "encoding/json" "errors" "fmt" "io" "maps" "slices" - "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -33,26 +33,59 @@ import ( "github.com/holiman/uint256" ) -//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -out bal_encoding_rlp_generated.go -type BlockAccessList -decoder +//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -out bal_encoding_rlp_generated.go -type AccountAccess -decoder // These are objects used as input for the access list encoding. They mirror // the spec format. -// BlockAccessList is the encoding format of ConstructionBlockAccessList. -type BlockAccessList struct { - Accesses []AccountAccess `ssz-max:"300000"` +// BlockAccessList is the encoding format of AccessListBuilder. +type BlockAccessList []AccountAccess + +func (e BlockAccessList) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + l := w.List() + for _, access := range e { + access.EncodeRLP(w) + } + w.ListEnd(l) + return w.Flush() +} + +func (e *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { + if _, err := dec.List(); err != nil { + return err + } + *e = (*e)[:0] + for dec.MoreDataInList() { + var access AccountAccess + if err := access.DecodeRLP(dec); err != nil { + return err + } + *e = append(*e, access) + } + dec.ListEnd() + return nil +} + +func (e *BlockAccessList) String() string { + var res bytes.Buffer + enc := json.NewEncoder(&res) + enc.SetIndent("", " ") + // TODO: check error + enc.Encode(e) + return res.String() } // Validate returns an error if the contents of the access list are not ordered // according to the spec or any code changes are contained which exceed protocol // max code size. -func (e *BlockAccessList) Validate() error { - if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int { +func (e BlockAccessList) Validate() error { + if !slices.IsSortedFunc(e, func(a, b AccountAccess) int { return bytes.Compare(a.Address[:], b.Address[:]) }) { return errors.New("block access list accounts not in lexicographic order") } - for _, entry := range e.Accesses { + for _, entry := range e { if err := entry.validate(); err != nil { return err } @@ -73,39 +106,28 @@ func (e *BlockAccessList) Hash() common.Hash { return crypto.Keccak256Hash(enc.Bytes()) } -// encodeBalance encodes the provided balance into 16-bytes. -func encodeBalance(val *uint256.Int) [16]byte { - valBytes := val.Bytes() - if len(valBytes) > 16 { - panic("can't encode value that is greater than 16 bytes in size") - } - var enc [16]byte - copy(enc[16-len(valBytes):], valBytes[:]) - return enc -} - // encodingBalanceChange is the encoding format of BalanceChange. type encodingBalanceChange struct { - TxIdx uint16 `ssz-size:"2"` - Balance [16]byte `ssz-size:"16"` + TxIdx uint16 `json:"txIndex"` + Balance *uint256.Int `json:"balance"` } // encodingAccountNonce is the encoding format of NonceChange. type encodingAccountNonce struct { - TxIdx uint16 `ssz-size:"2"` - Nonce uint64 `ssz-size:"8"` + TxIdx uint16 `json:"txIndex"` + Nonce uint64 `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 `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 `json:"slot"` + Accesses []encodingStorageWrite `json:"accesses"` } // validate returns an instance of the encoding-representation slot writes in @@ -119,14 +141,14 @@ func (e *encodingSlotWrites) validate() error { return errors.New("storage write tx indices not in order") } -// AccountAccess is the encoding format of ConstructionAccountAccess. +// AccountAccess is the encoding format of ConstructionAccountAccesses. 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 `json:"address,omitempty"` // 20-byte Ethereum address + StorageChanges []encodingSlotWrites `json:"storageChanges,omitempty"` // Storage changes (slot -> [tx_index -> new_value]) + StorageReads []common.Hash `json:"storageReads,omitempty"` // Read-only storage keys + BalanceChanges []encodingBalanceChange `json:"balanceChanges,omitempty"` // Balance changes ([tx_index -> post_balance]) + NonceChanges []encodingAccountNonce `json:"nonceChanges,omitempty"` // Nonce changes ([tx_index -> new_nonce]) + CodeChanges []CodeChange `json:"code,omitempty"` // CodeChanges changes ([tx_index -> new_code]) } // validate converts the account accesses out of encoding format. @@ -134,19 +156,42 @@ type AccountAccess struct { // spec, an error is returned. func (e *AccountAccess) validate() error { // Check the storage write slots are sorted in order - if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int { + if !slices.IsSortedFunc(e.StorageChanges, func(a, b encodingSlotWrites) int { return bytes.Compare(a.Slot[:], b.Slot[:]) }) { return errors.New("storage writes slots not in lexicographic order") } - for _, write := range e.StorageWrites { + for _, write := range e.StorageChanges { if err := write.validate(); err != nil { return err } } + // test case ideas: keys in both read/writes, duplicate keys in either read/writes + // ensure that the read and write key sets are distinct + readKeys := make(map[common.Hash]struct{}) + writeKeys := make(map[common.Hash]struct{}) + for _, readKey := range e.StorageReads { + if _, ok := readKeys[readKey]; ok { + return errors.New("duplicate read key") + } + readKeys[readKey] = struct{}{} + } + for _, write := range e.StorageChanges { + writeKey := write.Slot + if _, ok := writeKeys[writeKey]; ok { + return errors.New("duplicate write key") + } + writeKeys[writeKey] = struct{}{} + } + + for readKey := range readKeys { + if _, ok := writeKeys[readKey]; ok { + return errors.New("storage key reported in both read/write sets") + } + } // 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") @@ -167,9 +212,9 @@ func (e *AccountAccess) validate() error { } // Convert code change - if len(e.Code) == 1 { - if len(e.Code[0].Code) > params.MaxCodeSize { - return errors.New("code change contained oversized code") + for _, codeChange := range e.CodeChanges { + if len(codeChange.Code) > params.MaxCodeSize { + return fmt.Errorf("code change contained oversized code") } } return nil @@ -183,40 +228,39 @@ func (e *AccountAccess) Copy() AccountAccess { BalanceChanges: slices.Clone(e.BalanceChanges), NonceChanges: slices.Clone(e.NonceChanges), } - for _, storageWrite := range e.StorageWrites { - res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{ + for _, storageWrite := range e.StorageChanges { + res.StorageChanges = append(res.StorageChanges, encodingSlotWrites{ Slot: storageWrite.Slot, Accesses: slices.Clone(storageWrite.Accesses), }) } - if len(e.Code) == 1 { - res.Code = []CodeChange{ - { - e.Code[0].TxIndex, - bytes.Clone(e.Code[0].Code), - }, - } + for _, codeChange := range e.CodeChanges { + res.CodeChanges = append(res.CodeChanges, + CodeChange{ + codeChange.TxIdx, + bytes.Clone(codeChange.Code), + }) } return res } // EncodeRLP returns the RLP-encoded access list -func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error { - return b.toEncodingObj().EncodeRLP(wr) +func (c *AccessListBuilder) EncodeRLP(wr io.Writer) error { + return c.ToEncodingObj().EncodeRLP(wr) } -var _ rlp.Encoder = &ConstructionBlockAccessList{} +var _ rlp.Encoder = &AccessListBuilder{} -// toEncodingObj creates an instance of the ConstructionAccountAccess of the type that is +// toEncodingObj creates an instance of the ConstructionAccountAccesses of the type that is // used as input for the encoding. -func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess { +func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) AccountAccess { res := AccountAccess{ Address: addr, - StorageWrites: make([]encodingSlotWrites, 0), - StorageReads: make([][32]byte, 0), + StorageChanges: make([]encodingSlotWrites, 0), + StorageReads: make([]common.Hash, 0), BalanceChanges: make([]encodingBalanceChange, 0), NonceChanges: make([]encodingAccountNonce, 0), - Code: nil, + CodeChanges: make([]CodeChange, 0), } // Convert write slots @@ -237,7 +281,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc ValueAfter: slotWrites[index], }) } - res.StorageWrites = append(res.StorageWrites, obj) + res.StorageChanges = append(res.StorageChanges, obj) } // Convert read slots @@ -253,7 +297,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc for _, idx := range balanceIndices { res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{ TxIdx: idx, - Balance: encodeBalance(a.BalanceChanges[idx]), + Balance: new(uint256.Int).Set(a.BalanceChanges[idx]), }) } @@ -268,77 +312,31 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc } // Convert code change - if a.CodeChange != nil { - res.Code = []CodeChange{ - { - a.CodeChange.TxIndex, - bytes.Clone(a.CodeChange.Code), - }, - } + codeChangeIdxs := slices.Collect(maps.Keys(a.CodeChanges)) + slices.SortFunc(codeChangeIdxs, cmp.Compare[uint16]) + for _, idx := range codeChangeIdxs { + res.CodeChanges = append(res.CodeChanges, CodeChange{ + idx, + bytes.Clone(a.CodeChanges[idx].Code), + }) } 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 *AccessListBuilder) ToEncodingObj() *BlockAccessList { var addresses []common.Address - for addr := range b.Accounts { + for addr := range c.FinalizedAccesses { 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 = append(res, c.FinalizedAccesses[addr].toEncodingObj(addr)) } return &res } -func (e *BlockAccessList) PrettyPrint() string { - var res bytes.Buffer - printWithIndent := func(indent int, text string) { - fmt.Fprintf(&res, "%s%s\n", strings.Repeat(" ", indent), text) - } - for _, accountDiff := range e.Accesses { - printWithIndent(0, fmt.Sprintf("%x:", accountDiff.Address)) - - printWithIndent(1, "storage writes:") - for _, sWrite := range accountDiff.StorageWrites { - printWithIndent(2, fmt.Sprintf("%x:", sWrite.Slot)) - for _, access := range sWrite.Accesses { - printWithIndent(3, fmt.Sprintf("%d: %x", access.TxIdx, access.ValueAfter)) - } - } - - printWithIndent(1, "storage reads:") - for _, slot := range accountDiff.StorageReads { - printWithIndent(2, fmt.Sprintf("%x", slot)) - } - - printWithIndent(1, "balance changes:") - for _, change := range accountDiff.BalanceChanges { - balance := new(uint256.Int).SetBytes(change.Balance[:]).String() - printWithIndent(2, fmt.Sprintf("%d: %s", change.TxIdx, balance)) - } - - printWithIndent(1, "nonce changes:") - for _, change := range accountDiff.NonceChanges { - printWithIndent(2, fmt.Sprintf("%d: %d", change.TxIdx, change.Nonce)) - } - - if len(accountDiff.Code) > 0 { - printWithIndent(1, "code:") - printWithIndent(2, fmt.Sprintf("%d: %x", accountDiff.Code[0].TxIndex, accountDiff.Code[0].Code)) - } - } - return res.String() -} - -// Copy returns a deep copy of the access list -func (e *BlockAccessList) Copy() (res BlockAccessList) { - for _, accountAccess := range e.Accesses { - res.Accesses = append(res.Accesses, accountAccess.Copy()) - } - return -} +type ContractCode []byte diff --git a/core/types/bal/bal_encoding_json.go b/core/types/bal/bal_encoding_json.go new file mode 100644 index 00000000000..bf25345b5e0 --- /dev/null +++ b/core/types/bal/bal_encoding_json.go @@ -0,0 +1,107 @@ +package bal + +import ( + "encoding/json" + "fmt" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rlp" +) + +func (c *ContractCode) MarshalJSON() ([]byte, error) { + hexStr := fmt.Sprintf("%x", *c) + return json.Marshal(hexStr) +} +func (e encodingBalanceChange) MarshalJSON() ([]byte, error) { + type Alias encodingBalanceChange + return json.Marshal(&struct { + TxIdx string `json:"txIndex"` + *Alias + }{ + TxIdx: fmt.Sprintf("0x%x", e.TxIdx), + Alias: (*Alias)(&e), + }) +} + +func (e *encodingBalanceChange) UnmarshalJSON(data []byte) error { + type Alias encodingBalanceChange + aux := &struct { + TxIdx string `json:"txIndex"` + *Alias + }{ + Alias: (*Alias)(e), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + if len(aux.TxIdx) >= 2 && aux.TxIdx[:2] == "0x" { + if _, err := fmt.Sscanf(aux.TxIdx, "0x%x", &e.TxIdx); err != nil { + return err + } + } + return nil +} +func (e encodingAccountNonce) MarshalJSON() ([]byte, error) { + type Alias encodingAccountNonce + return json.Marshal(&struct { + TxIdx string `json:"txIndex"` + Nonce string `json:"nonce"` + *Alias + }{ + TxIdx: fmt.Sprintf("0x%x", e.TxIdx), + Nonce: fmt.Sprintf("0x%x", e.Nonce), + Alias: (*Alias)(&e), + }) +} + +func (e *encodingAccountNonce) UnmarshalJSON(data []byte) error { + type Alias encodingAccountNonce + aux := &struct { + TxIdx string `json:"txIndex"` + Nonce string `json:"nonce"` + *Alias + }{ + Alias: (*Alias)(e), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + if len(aux.TxIdx) >= 2 && aux.TxIdx[:2] == "0x" { + if _, err := fmt.Sscanf(aux.TxIdx, "0x%x", &e.TxIdx); err != nil { + return err + } + } + if len(aux.Nonce) >= 2 && aux.Nonce[:2] == "0x" { + if _, err := fmt.Sscanf(aux.Nonce, "0x%x", &e.Nonce); err != nil { + return err + } + } + return nil +} + +// UnmarshalJSON implements json.Unmarshaler to decode from RLP hex bytes +func (b *BlockAccessList) UnmarshalJSON(input []byte) error { + // Handle both hex string and object formats + var hexBytes hexutil.Bytes + if err := json.Unmarshal(input, &hexBytes); err == nil { + // It's a hex string, decode from RLP + return rlp.DecodeBytes(hexBytes, b) + } + + // Otherwise try to unmarshal as structured JSON + var tmp []AccountAccess + if err := json.Unmarshal(input, &tmp); err != nil { + return err + } + *b = BlockAccessList(tmp) + return nil +} + +// MarshalJSON implements json.Marshaler to encode as RLP hex bytes +func (b BlockAccessList) MarshalJSON() ([]byte, error) { + // Encode to RLP then to hex + rlpBytes, err := rlp.EncodeToBytes(b) + if err != nil { + return nil, err + } + return json.Marshal(hexutil.Bytes(rlpBytes)) +} diff --git a/core/types/bal/bal_encoding_rlp_generated.go b/core/types/bal/bal_encoding_rlp_generated.go index 0d523953290..5a99e1b8009 100644 --- a/core/types/bal/bal_encoding_rlp_generated.go +++ b/core/types/bal/bal_encoding_rlp_generated.go @@ -2,275 +2,254 @@ package bal +import "github.com/ethereum/go-ethereum/common" import "github.com/ethereum/go-ethereum/rlp" +import "github.com/holiman/uint256" import "io" -func (obj *BlockAccessList) EncodeRLP(_w io.Writer) error { +func (obj *AccountAccess) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) _tmp0 := w.List() + w.WriteBytes(obj.Address[:]) _tmp1 := w.List() - for _, _tmp2 := range obj.Accesses { + for _, _tmp2 := range obj.StorageChanges { _tmp3 := w.List() - w.WriteBytes(_tmp2.Address[:]) + w.WriteBytes(_tmp2.Slot[:]) _tmp4 := w.List() - for _, _tmp5 := range _tmp2.StorageWrites { + for _, _tmp5 := range _tmp2.Accesses { _tmp6 := w.List() - w.WriteBytes(_tmp5.Slot[:]) - _tmp7 := w.List() - for _, _tmp8 := range _tmp5.Accesses { - _tmp9 := w.List() - w.WriteUint64(uint64(_tmp8.TxIdx)) - w.WriteBytes(_tmp8.ValueAfter[:]) - w.ListEnd(_tmp9) - } - w.ListEnd(_tmp7) + w.WriteUint64(uint64(_tmp5.TxIdx)) + w.WriteBytes(_tmp5.ValueAfter[:]) w.ListEnd(_tmp6) } w.ListEnd(_tmp4) - _tmp10 := w.List() - for _, _tmp11 := range _tmp2.StorageReads { - w.WriteBytes(_tmp11[:]) - } - w.ListEnd(_tmp10) - _tmp12 := w.List() - for _, _tmp13 := range _tmp2.BalanceChanges { - _tmp14 := w.List() - w.WriteUint64(uint64(_tmp13.TxIdx)) - w.WriteBytes(_tmp13.Balance[:]) - w.ListEnd(_tmp14) - } - w.ListEnd(_tmp12) - _tmp15 := w.List() - for _, _tmp16 := range _tmp2.NonceChanges { - _tmp17 := w.List() - w.WriteUint64(uint64(_tmp16.TxIdx)) - w.WriteUint64(_tmp16.Nonce) - w.ListEnd(_tmp17) - } - w.ListEnd(_tmp15) - _tmp18 := w.List() - for _, _tmp19 := range _tmp2.Code { - _tmp20 := w.List() - w.WriteUint64(uint64(_tmp19.TxIndex)) - w.WriteBytes(_tmp19.Code) - w.ListEnd(_tmp20) - } - w.ListEnd(_tmp18) w.ListEnd(_tmp3) } w.ListEnd(_tmp1) + _tmp7 := w.List() + for _, _tmp8 := range obj.StorageReads { + w.WriteBytes(_tmp8[:]) + } + w.ListEnd(_tmp7) + _tmp9 := w.List() + for _, _tmp10 := range obj.BalanceChanges { + _tmp11 := w.List() + w.WriteUint64(uint64(_tmp10.TxIdx)) + if _tmp10.Balance == nil { + w.Write(rlp.EmptyString) + } else { + w.WriteUint256(_tmp10.Balance) + } + w.ListEnd(_tmp11) + } + w.ListEnd(_tmp9) + _tmp12 := w.List() + for _, _tmp13 := range obj.NonceChanges { + _tmp14 := w.List() + w.WriteUint64(uint64(_tmp13.TxIdx)) + w.WriteUint64(_tmp13.Nonce) + w.ListEnd(_tmp14) + } + w.ListEnd(_tmp12) + _tmp15 := w.List() + for _, _tmp16 := range obj.CodeChanges { + _tmp17 := w.List() + w.WriteUint64(uint64(_tmp16.TxIdx)) + w.WriteBytes(_tmp16.Code) + w.ListEnd(_tmp17) + } + w.ListEnd(_tmp15) w.ListEnd(_tmp0) return w.Flush() } -func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { - var _tmp0 BlockAccessList +func (obj *AccountAccess) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 AccountAccess { if _, err := dec.List(); err != nil { return err } - // Accesses: - var _tmp1 []AccountAccess + // Address: + var _tmp1 common.Address + if err := dec.ReadBytes(_tmp1[:]); err != nil { + return err + } + _tmp0.Address = _tmp1 + // StorageChanges: + var _tmp2 []encodingSlotWrites if _, err := dec.List(); err != nil { return err } for dec.MoreDataInList() { - var _tmp2 AccountAccess + var _tmp3 encodingSlotWrites { if _, err := dec.List(); err != nil { return err } - // Address: - var _tmp3 [20]byte - if err := dec.ReadBytes(_tmp3[:]); err != nil { + // Slot: + var _tmp4 common.Hash + if err := dec.ReadBytes(_tmp4[:]); err != nil { return err } - _tmp2.Address = _tmp3 - // StorageWrites: - var _tmp4 []encodingSlotWrites + _tmp3.Slot = _tmp4 + // Accesses: + var _tmp5 []encodingStorageWrite if _, err := dec.List(); err != nil { return err } for dec.MoreDataInList() { - var _tmp5 encodingSlotWrites + var _tmp6 encodingStorageWrite { if _, err := dec.List(); err != nil { return err } - // Slot: - var _tmp6 [32]byte - if err := dec.ReadBytes(_tmp6[:]); err != nil { - return err - } - _tmp5.Slot = _tmp6 - // Accesses: - var _tmp7 []encodingStorageWrite - if _, err := dec.List(); err != nil { + // TxIdx: + _tmp7, err := dec.Uint16() + if err != nil { return err } - for dec.MoreDataInList() { - var _tmp8 encodingStorageWrite - { - if _, err := dec.List(); err != nil { - return err - } - // TxIdx: - _tmp9, err := dec.Uint16() - if err != nil { - return err - } - _tmp8.TxIdx = _tmp9 - // ValueAfter: - var _tmp10 [32]byte - if err := dec.ReadBytes(_tmp10[:]); err != nil { - return err - } - _tmp8.ValueAfter = _tmp10 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp7 = append(_tmp7, _tmp8) - } - if err := dec.ListEnd(); err != nil { + _tmp6.TxIdx = _tmp7 + // ValueAfter: + var _tmp8 common.Hash + if err := dec.ReadBytes(_tmp8[:]); err != nil { return err } - _tmp5.Accesses = _tmp7 + _tmp6.ValueAfter = _tmp8 if err := dec.ListEnd(); err != nil { return err } } - _tmp4 = append(_tmp4, _tmp5) + _tmp5 = append(_tmp5, _tmp6) } if err := dec.ListEnd(); err != nil { return err } - _tmp2.StorageWrites = _tmp4 - // StorageReads: - var _tmp11 [][32]byte - if _, err := dec.List(); err != nil { - return err - } - for dec.MoreDataInList() { - var _tmp12 [32]byte - if err := dec.ReadBytes(_tmp12[:]); err != nil { - return err - } - _tmp11 = append(_tmp11, _tmp12) - } + _tmp3.Accesses = _tmp5 if err := dec.ListEnd(); err != nil { return err } - _tmp2.StorageReads = _tmp11 - // BalanceChanges: - var _tmp13 []encodingBalanceChange + } + _tmp2 = append(_tmp2, _tmp3) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp0.StorageChanges = _tmp2 + // StorageReads: + var _tmp9 []common.Hash + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp10 common.Hash + if err := dec.ReadBytes(_tmp10[:]); err != nil { + return err + } + _tmp9 = append(_tmp9, _tmp10) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp0.StorageReads = _tmp9 + // BalanceChanges: + var _tmp11 []encodingBalanceChange + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp12 encodingBalanceChange + { if _, err := dec.List(); err != nil { return err } - for dec.MoreDataInList() { - var _tmp14 encodingBalanceChange - { - if _, err := dec.List(); err != nil { - return err - } - // TxIdx: - _tmp15, err := dec.Uint16() - if err != nil { - return err - } - _tmp14.TxIdx = _tmp15 - // Balance: - var _tmp16 [16]byte - if err := dec.ReadBytes(_tmp16[:]); err != nil { - return err - } - _tmp14.Balance = _tmp16 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp13 = append(_tmp13, _tmp14) + // TxIdx: + _tmp13, err := dec.Uint16() + if err != nil { + return err } + _tmp12.TxIdx = _tmp13 + // Balance: + var _tmp14 uint256.Int + if err := dec.ReadUint256(&_tmp14); err != nil { + return err + } + _tmp12.Balance = &_tmp14 if err := dec.ListEnd(); err != nil { return err } - _tmp2.BalanceChanges = _tmp13 - // NonceChanges: - var _tmp17 []encodingAccountNonce + } + _tmp11 = append(_tmp11, _tmp12) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp0.BalanceChanges = _tmp11 + // NonceChanges: + var _tmp15 []encodingAccountNonce + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp16 encodingAccountNonce + { if _, err := dec.List(); err != nil { return err } - for dec.MoreDataInList() { - var _tmp18 encodingAccountNonce - { - if _, err := dec.List(); err != nil { - return err - } - // TxIdx: - _tmp19, err := dec.Uint16() - if err != nil { - return err - } - _tmp18.TxIdx = _tmp19 - // Nonce: - _tmp20, err := dec.Uint64() - if err != nil { - return err - } - _tmp18.Nonce = _tmp20 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp17 = append(_tmp17, _tmp18) + // TxIdx: + _tmp17, err := dec.Uint16() + if err != nil { + return err + } + _tmp16.TxIdx = _tmp17 + // Nonce: + _tmp18, err := dec.Uint64() + if err != nil { + return err } + _tmp16.Nonce = _tmp18 if err := dec.ListEnd(); err != nil { return err } - _tmp2.NonceChanges = _tmp17 - // Code: - var _tmp21 []CodeChange + } + _tmp15 = append(_tmp15, _tmp16) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp0.NonceChanges = _tmp15 + // CodeChanges: + var _tmp19 []CodeChange + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp20 CodeChange + { if _, err := dec.List(); err != nil { return err } - for dec.MoreDataInList() { - var _tmp22 CodeChange - { - if _, err := dec.List(); err != nil { - return err - } - // TxIndex: - _tmp23, err := dec.Uint16() - if err != nil { - return err - } - _tmp22.TxIndex = _tmp23 - // Code: - _tmp24, err := dec.Bytes() - if err != nil { - return err - } - _tmp22.Code = _tmp24 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp21 = append(_tmp21, _tmp22) + // TxIdx: + _tmp21, err := dec.Uint16() + if err != nil { + return err } - if err := dec.ListEnd(); err != nil { + _tmp20.TxIdx = _tmp21 + // Code: + _tmp22, err := dec.Bytes() + if err != nil { return err } - _tmp2.Code = _tmp21 + _tmp20.Code = _tmp22 if err := dec.ListEnd(); err != nil { return err } } - _tmp1 = append(_tmp1, _tmp2) + _tmp19 = append(_tmp19, _tmp20) } if err := dec.ListEnd(); err != nil { return err } - _tmp0.Accesses = _tmp1 + _tmp0.CodeChanges = _tmp19 if err := dec.ListEnd(); err != nil { return err } diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go index 29414e414e8..1e49ada31aa 100644 --- a/core/types/bal/bal_test.go +++ b/core/types/bal/bal_test.go @@ -36,9 +36,9 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool { return true } -func makeTestConstructionBAL() *ConstructionBlockAccessList { - return &ConstructionBlockAccessList{ - map[common.Address]*ConstructionAccountAccess{ +func makeTestConstructionBAL() *AccessListBuilder { + return &AccessListBuilder{ + map[common.Address]*ConstructionAccountAccesses{ common.BytesToAddress([]byte{0xff, 0xff}): { StorageWrites: map[common.Hash]map[uint16]common.Hash{ common.BytesToHash([]byte{0x01}): { @@ -60,10 +60,10 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList { 1: 2, 2: 6, }, - CodeChange: &CodeChange{ - TxIndex: 0, - Code: common.Hex2Bytes("deadbeef"), - }, + CodeChanges: map[uint16]CodeChange{0: { + TxIdx: 0, + Code: common.Hex2Bytes("deadbeef"), + }}, }, common.BytesToAddress([]byte{0xff, 0xff, 0xff}): { StorageWrites: map[common.Hash]map[uint16]common.Hash{ @@ -102,10 +102,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 +113,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 +144,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[:]) }) } @@ -152,7 +152,7 @@ func makeTestAccountAccess(sort bool) AccountAccess { for i := 0; i < 5; i++ { balances = append(balances, encodingBalanceChange{ TxIdx: uint16(2 * i), - Balance: [16]byte(testrand.Bytes(16)), + Balance: new(uint256.Int).SetBytes(testrand.Bytes(32)), }) } if sort { @@ -175,14 +175,14 @@ func makeTestAccountAccess(sort bool) AccountAccess { return AccountAccess{ Address: [20]byte(testrand.Bytes(20)), - StorageWrites: storageWrites, + StorageChanges: storageWrites, StorageReads: storageReads, BalanceChanges: balances, NonceChanges: nonces, - Code: []CodeChange{ + CodeChanges: []CodeChange{ { - TxIndex: 100, - Code: testrand.Bytes(256), + TxIdx: 100, + Code: testrand.Bytes(256), }, }, } @@ -191,10 +191,10 @@ func makeTestAccountAccess(sort bool) AccountAccess { func makeTestBAL(sort bool) BlockAccessList { list := BlockAccessList{} for i := 0; i < 5; i++ { - list.Accesses = append(list.Accesses, makeTestAccountAccess(sort)) + list = append(list, makeTestAccountAccess(sort)) } if sort { - slices.SortFunc(list.Accesses, func(a, b AccountAccess) int { + slices.SortFunc(list, func(a, b AccountAccess) int { return bytes.Compare(a.Address[:], b.Address[:]) }) } @@ -214,7 +214,7 @@ func TestBlockAccessListCopy(t *testing.T) { } // Make sure the mutations on copy won't affect the origin - for _, aa := range cpyCpy.Accesses { + for _, aa := range cpyCpy { for i := 0; i < len(aa.StorageReads); i++ { aa.StorageReads[i] = [32]byte(testrand.Bytes(32)) } @@ -245,8 +245,11 @@ 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) } } + +// BALReader test ideas +// * BAL which doesn't have any pre-tx system contracts should return an empty state diff at idx 0 diff --git a/core/types/bal_blocks_test.go b/core/types/bal_blocks_test.go new file mode 100644 index 00000000000..9500cf8ca74 --- /dev/null +++ b/core/types/bal_blocks_test.go @@ -0,0 +1,32 @@ +package types + +import ( + "bytes" + "fmt" + "github.com/ethereum/go-ethereum/rlp" + "io" + "os" + "testing" +) + +func TestBALDecoding(t *testing.T) { + var ( + err error + data []byte + ) + data, err = os.ReadFile("blocks_bal_one.rlp") + if err != nil { + t.Fatalf("error opening file: %v", err) + } + reader := bytes.NewReader(data) + stream := rlp.NewStream(reader, 0) + var blocks Block + for i := 0; err == nil; i++ { + fmt.Printf("decode %d\n", i) + err = stream.Decode(&blocks) + if err != nil && err != io.EOF { + t.Fatalf("error decoding blocks: %v", err) + } + fmt.Printf("block number is %d\n", blocks.NumberU64()) + } +} diff --git a/core/types/block.go b/core/types/block.go index b5b6468a131..3ba7ce7f47d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -28,6 +28,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 +108,9 @@ type Header struct { // RequestsHash was added by EIP-7685 and is ignored in legacy headers. RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + + // BlockAccessListHash was added by EIP-7928 and is ignored in legacy headers. + BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"` } // 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 @@ -229,7 +237,8 @@ type extblock struct { Header *Header Txs []*Transaction Uncles []*Header - Withdrawals []*Withdrawal `rlp:"optional"` + Withdrawals []*Withdrawal `rlp:"optional"` + AccessList *bal.BlockAccessList `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 ListHasher b.withdrawals = slices.Clone(withdrawals) } + if body.AccessList != nil { + balHash := body.AccessList.Hash() + b.header.BlockAccessListHash = &balHash + b.accessList = body.AccessList + } + return b } @@ -334,12 +349,14 @@ 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 + ) _, 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 + b.header, b.uncles, b.transactions, b.withdrawals, b.accessList = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals, eb.AccessList b.size.Store(rlp.ListSize(size)) return nil } @@ -351,13 +368,14 @@ func (b *Block) EncodeRLP(w io.Writer) error { Txs: b.transactions, Uncles: b.uncles, Withdrawals: b.withdrawals, + AccessList: b.accessList, }) } // 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 +526,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]) } @@ -520,6 +542,7 @@ func (b *Block) WithWitness(witness *ExecutionWitness) *Block { transactions: b.transactions, uncles: b.uncles, withdrawals: b.withdrawals, + accessList: b.accessList, witness: witness, } } diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 0af12500bdf..66856bba361 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -16,28 +16,29 @@ var _ = (*headerMarshaling)(nil) // MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase common.Address `json:"miner"` - Root common.Hash `json:"stateRoot" gencodec:"required"` - TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` - Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"` + Hash common.Hash `json:"hash"` } var enc Header enc.ParentHash = h.ParentHash @@ -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.BlockAccessListHash = h.BlockAccessListHash enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -68,27 +70,28 @@ func (h Header) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase *common.Address `json:"miner"` - Root *common.Hash `json:"stateRoot" gencodec:"required"` - TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom *Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` + BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"` } 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.BlockAccessListHash != nil { + h.BlockAccessListHash = dec.BlockAccessListHash + } return nil } diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go index c79aa8a2502..e68e8927c28 100644 --- a/core/types/gen_header_rlp.go +++ b/core/types/gen_header_rlp.go @@ -43,7 +43,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { _tmp4 := obj.ExcessBlobGas != nil _tmp5 := obj.ParentBeaconRoot != nil _tmp6 := obj.RequestsHash != nil - if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { + _tmp7 := obj.BlockAccessListHash != nil + if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 { if obj.BaseFee == nil { w.Write(rlp.EmptyString) } else { @@ -53,41 +54,48 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteBigInt(obj.BaseFee) } } - if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 { + if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 { if obj.WithdrawalsHash == nil { w.Write([]byte{0x80}) } else { w.WriteBytes(obj.WithdrawalsHash[:]) } } - if _tmp3 || _tmp4 || _tmp5 || _tmp6 { + if _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 { if obj.BlobGasUsed == nil { w.Write([]byte{0x80}) } else { w.WriteUint64((*obj.BlobGasUsed)) } } - if _tmp4 || _tmp5 || _tmp6 { + if _tmp4 || _tmp5 || _tmp6 || _tmp7 { if obj.ExcessBlobGas == nil { w.Write([]byte{0x80}) } else { w.WriteUint64((*obj.ExcessBlobGas)) } } - if _tmp5 || _tmp6 { + if _tmp5 || _tmp6 || _tmp7 { if obj.ParentBeaconRoot == nil { w.Write([]byte{0x80}) } else { w.WriteBytes(obj.ParentBeaconRoot[:]) } } - if _tmp6 { + if _tmp6 || _tmp7 { if obj.RequestsHash == nil { w.Write([]byte{0x80}) } else { w.WriteBytes(obj.RequestsHash[:]) } } + if _tmp7 { + if obj.BlockAccessListHash == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.BlockAccessListHash[:]) + } + } w.ListEnd(_tmp0) return w.Flush() } diff --git a/core/vm/evm.go b/core/vm/evm.go index 88ef1cf121e..b9a96e8d711 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -77,6 +77,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 @@ -470,25 +471,32 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b // create creates a new contract using code as deployment code. func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { + // Depth check execution. Fail if we're trying to execute above the + // limit. + var nonce uint64 + if evm.depth > int(params.CallCreateDepth) { + err = ErrDepth + } else if !evm.Context.CanTransfer(evm.StateDB, caller, value) { + err = ErrInsufficientBalance + } else { + nonce = evm.StateDB.GetNonce(caller) + if nonce+1 < nonce { + err = ErrNonceUintOverflow + } + } + + if err == nil { + evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator) + } if evm.Config.Tracer != nil { evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig()) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } - // Depth check execution. Fail if we're trying to execute above the - // limit. - if evm.depth > int(params.CallCreateDepth) { - return nil, common.Address{}, gas, ErrDepth - } - if !evm.Context.CanTransfer(evm.StateDB, caller, value) { - return nil, common.Address{}, gas, ErrInsufficientBalance - } - nonce := evm.StateDB.GetNonce(caller) - if nonce+1 < nonce { - return nil, common.Address{}, gas, ErrNonceUintOverflow + if err != nil { + return nil, common.Address{}, gas, err } - evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator) // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { @@ -514,6 +522,7 @@ 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 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code (storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage @@ -601,7 +610,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b } } - evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation) + if len(ret) > 0 { + evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation) + } return ret, nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 44d3e81a9cf..bff06faa5cb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -887,7 +887,9 @@ func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) - evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) { + evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + } evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { @@ -906,8 +908,23 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) - evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) - evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + + createdInTx := !evm.StateDB.ExistBeforeCurTx(scope.Contract.Address()) + + if createdInTx { + // if the contract is not preexisting, the balance is immediately burned on selfdestruct-to-self + evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) + if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) { + evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + } + } else { + // if the contract is preexisting, the balance isn't burned on selfdestruct-to-self + if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) { + evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) + evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + } + } + evm.StateDB.SelfDestruct6780(scope.Contract.Address()) if tracer := evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { diff --git a/core/vm/interface.go b/core/vm/interface.go index d7f4c10e1f5..2586154e93a 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -71,6 +71,8 @@ type StateDB interface { // Exist reports whether the given account exists in state. // Notably this also returns true for self-destructed accounts within the current transaction. Exist(common.Address) bool + ExistBeforeCurTx(addr common.Address) bool + // Empty returns whether the given account is empty. Empty // is defined according to EIP161 (balance = nonce = code = 0). Empty(common.Address) bool @@ -99,6 +101,8 @@ type StateDB interface { AccessEvents() *state.AccessEvents + TxIndex() int + // Finalise must be invoked at the end of a transaction Finalise(bool) } diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index 89a2ebf6f4f..4157dddcffa 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.IsAmsterdam: + return newPragueInstructionSet(), errors.New("glamsterdam-fork not defined yet") case rules.IsOsaka: return newOsakaInstructionSet(), nil case rules.IsPrague: diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 085b018e4c4..669c8b2a349 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -18,7 +18,6 @@ package vm import ( "errors" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" @@ -123,6 +122,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { evm.StateDB.AddAddressToAccessList(addr) + var overflow bool // We charge (cold-warm), since 'warm' is already charged as constantGas if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index a001d816233..fbdc0efbb0b 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -676,7 +676,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 892e1032134..2d1adb20e23 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -17,9 +17,11 @@ package eth import ( + "bytes" "context" "errors" "fmt" + "github.com/ethereum/go-ethereum/core/types/bal" "time" "github.com/ethereum/go-ethereum/common" @@ -505,7 +507,7 @@ func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn) } - result, err := bc.ProcessBlock(parent.Root, block, false, true) + result, err := bc.ProcessBlock(parent.Root, block, false, true, false, false) if err != nil { return nil, err } @@ -525,10 +527,40 @@ func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWit return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash) } - result, err := bc.ProcessBlock(parent.Root, block, false, true) + result, err := bc.ProcessBlock(parent.Root, block, false, true, false, false) if err != nil { return nil, err } return result.Witness().ToExtWitness(), nil } + +// GetBlockAccessList returns a block access list for the given number/hash +// or nil if one does not exist. +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 +} + +// GetEncodedBlockAccessList returns a block access list corresponding to a +// block number/hash in RLP-encoded form. It returns nil if one does not exist. +func (api *DebugAPI) GetEncodedBlockAccessList(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 85095618222..a280dfdeeb1 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -244,6 +244,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // - DATADIR/triedb/verkle.journal TrieJournalDirectory: stack.ResolvePath("triedb"), StateSizeTracking: config.EnableStateSizeTracking, + EnableBALForTesting: config.ExperimentalBAL, } ) if config.VMTrace != "" { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 75b263bf6b6..ffea35a69fc 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -81,6 +81,43 @@ const ( beaconUpdateWarnFrequency = 5 * time.Minute ) +// All methods provided over the engine endpoint. +var caps = []string{ + "engine_forkchoiceUpdatedV1", + "engine_forkchoiceUpdatedV2", + "engine_forkchoiceUpdatedV3", + "engine_forkchoiceUpdatedWithWitnessV1", + "engine_forkchoiceUpdatedWithWitnessV2", + "engine_forkchoiceUpdatedWithWitnessV3", + "engine_exchangeTransitionConfigurationV1", + "engine_getPayloadV1", + "engine_getPayloadV2", + "engine_getPayloadV3", + "engine_getPayloadV4", + "engine_getPayloadV5", + "engine_getPayloadV6", + "engine_getBlobsV1", + "engine_getBlobsV2", + "engine_newPayloadV1", + "engine_newPayloadV2", + "engine_newPayloadV3", + "engine_newPayloadV4", + "engine_newPayloadV5", + "engine_newPayloadWithWitnessV1", + "engine_newPayloadWithWitnessV2", + "engine_newPayloadWithWitnessV3", + "engine_newPayloadWithWitnessV4", + "engine_executeStatelessPayloadV1", + "engine_executeStatelessPayloadV2", + "engine_executeStatelessPayloadV3", + "engine_executeStatelessPayloadV4", + "engine_getPayloadBodiesByHashV1", + "engine_getPayloadBodiesByHashV2", + "engine_getPayloadBodiesByRangeV1", + "engine_getPayloadBodiesByRangeV2", + "engine_getClientVersionV1", +} + var ( // Number of blobs requested via getBlobsV2 getBlobsRequestedCounter = metrics.NewRegisteredCounter("engine/getblobs/requested", nil) @@ -212,9 +249,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa return engine.STATUS_INVALID, attributesErr("missing withdrawals") case params.BeaconRoot == nil: return engine.STATUS_INVALID, attributesErr("missing beacon root") - case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5, forks.Amsterdam): return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun/prague/osaka payloads") } + + if api.checkFork(params.Timestamp, forks.Amsterdam) { + return api.forkchoiceUpdated(update, params, engine.PayloadV4, false) + } } // TODO(matt): the spec requires that fcu is applied when called on a valid // hash, even if params are wrong. To do this we need to split up @@ -460,6 +501,14 @@ func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.Execu return api.getPayload(payloadID, false) } +// GetPayloadV6 returns a cached payload by id. +func (api *ConsensusAPI) GetPayloadV6(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { + if !payloadID.Is(engine.PayloadV4) { + return nil, engine.UnsupportedFork + } + return api.getPayload(payloadID, false) +} + func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) { log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID) data := api.localBlocks.get(payloadID, full) @@ -665,6 +714,33 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } +// NewPayloadV5 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. +func (api *ConsensusAPI) NewPayloadV5(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return invalidStatus, paramsErr("nil executionRequests post-prague") + case params.BlockAccessList == nil: + return invalidStatus, paramsErr("nil block access list post-amsterdam") + case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.Amsterdam): + return invalidStatus, unsupportedForkErr("newPayloadV5 must only be called for amsterdam payloads") + } + requests := convertRequests(executionRequests) + if err := validateRequests(requests); err != nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) + } + return api.newPayload(params, versionedHashes, beaconRoot, requests, false) +} + func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (engine.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // diff --git a/eth/downloader/fetchers_concurrent_bodies.go b/eth/downloader/fetchers_concurrent_bodies.go index 56359b33c94..26b2032b3de 100644 --- a/eth/downloader/fetchers_concurrent_bodies.go +++ b/eth/downloader/fetchers_concurrent_bodies.go @@ -88,10 +88,10 @@ func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan // deliver is responsible for taking a generic response packet from the concurrent // fetcher, unpacking the body data and delivering it to the downloader's queue. func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { - txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesResponse).Unpack() - hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes} + txs, uncles, withdrawals, accessLists := packet.Res.(*eth.BlockBodiesResponse).Unpack() + hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes, access list hashes} - accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2]) + accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2], accessLists, hashsets[3]) switch { case err == nil && len(txs) == 0: peer.log.Trace("Requested bodies delivered") diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 9fe169d5f74..ac527b6d93d 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -22,6 +22,7 @@ package downloader import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/core/types/bal" "sync" "sync/atomic" "time" @@ -564,6 +565,7 @@ func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash, withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash, + blockAccessLists []*bal.BlockAccessList, accessListHashes []common.Hash, ) (int, error) { q.lock.Lock() defer q.lock.Unlock() @@ -588,6 +590,19 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH return errInvalidBody } } + if header.BlockAccessListHash == nil { + // nil hash means that access list should not be present in body + if blockAccessLists[index] != nil { + return errInvalidBody + } + } else { // non-nil hash: body must have access list + if blockAccessLists[index] == nil { + return errInvalidBody + } + if accessListHashes[index] != header.Hash() { + return errInvalidBody + } + } // Blocks must have a number of blobs corresponding to the header gas usage, // and zero before the Cancun hardfork. var blobs int diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index c4a0956b3b4..31f00ec6938 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -189,6 +189,13 @@ type Config struct { // EIP-7966: eth_sendRawTransactionSync timeouts TxSyncDefaultTimeout time.Duration `toml:",omitempty"` TxSyncMaxTimeout time.Duration `toml:",omitempty"` + + // ExperimentalBAL enables EIP-7928 block access list creation during execution + // of post Cancun blocks, and persistence via embedding the BAL in the block body. + // + // TODO: also note that it will cause execution of blocks with access lists to base + // their execution on the BAL. + ExperimentalBAL bool `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index aad3353d88d..6d99944b2cf 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -381,6 +381,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { txsHashes = make([]common.Hash, len(res.BlockBodiesResponse)) uncleHashes = make([]common.Hash, len(res.BlockBodiesResponse)) withdrawalHashes = make([]common.Hash, len(res.BlockBodiesResponse)) + accessListHashes = make([]common.Hash, len(res.BlockBodiesResponse)) ) hasher := trie.NewStackTrie(nil) for i, body := range res.BlockBodiesResponse { @@ -389,8 +390,11 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { if body.Withdrawals != nil { withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) } + if body.AccessList != nil { + accessListHashes[i] = body.AccessList.Hash() + } } - return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes} + return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes, accessListHashes} } return peer.dispatchResponse(&Response{ id: res.RequestId, diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 7c41e7a9963..91dfca60390 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -19,6 +19,7 @@ package eth import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/core/types/bal" "io" "math/big" @@ -239,20 +240,22 @@ type BlockBody struct { Transactions []*types.Transaction // Transactions contained within a block Uncles []*types.Header // Uncles contained within a block Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block + AccessList *bal.BlockAccessList `rlp:"optional"` } // Unpack retrieves the transactions and uncles from the range packet and returns // them in a split flat format that's more consistent with the internal data structures. -func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) { +func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal, []*bal.BlockAccessList) { var ( txset = make([][]*types.Transaction, len(*p)) uncleset = make([][]*types.Header, len(*p)) withdrawalset = make([][]*types.Withdrawal, len(*p)) + accessListSet = make([]*bal.BlockAccessList, len(*p)) ) for i, body := range *p { - txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals + txset[i], uncleset[i], withdrawalset[i], accessListSet[i] = body.Transactions, body.Uncles, body.Withdrawals, body.AccessList } - return txset, uncleset, withdrawalset + return txset, uncleset, withdrawalset, accessListSet } // GetReceiptsRequest represents a block receipts query. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index eb7a34474cf..1ede0f5904c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -975,6 +975,9 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param if block.Withdrawals() != nil { fields["withdrawals"] = block.Withdrawals() } + if block.Body().AccessList != nil { + fields["accessList"] = block.Body().AccessList + } return fields } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 0d1a59b371a..8af24d3a191 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -347,7 +347,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals} chainHeadReader := &simChainHeadReader{ctx, sim.b} - b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts) + b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts, nil) if err != nil { return nil, nil, nil, err } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 0aedffe2307..2810d1e3a4c 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -474,6 +474,16 @@ web3._extend({ params: 1, inputFormatter: [null], }), + new web3._extend.Method({ + name: 'getBlockAccessList', + call: 'debug_getBlockAccessList', + params: 1 + }), + new web3._extend.Method({ + name: 'getEncodedBlockAccessList', + call: 'debug_getEncodedBlockAccessList', + params: 1 + }), ], properties: [] }); diff --git a/miner/worker.go b/miner/worker.go index c0574eac238..aa05acde827 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,6 +19,7 @@ package miner import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/core/tracing" "math/big" "sync/atomic" "time" @@ -60,7 +61,8 @@ type environment struct { sidecars []*types.BlobTxSidecar blobs int - witness *stateless.Witness + witness *stateless.Witness + alTracer *core.BlockAccessListTracer } // txFits reports whether the transaction fits into the block size limit. @@ -134,6 +136,9 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay } } body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals} + if work.alTracer != nil { + body.AccessList = work.alTracer.AccessList().ToEncodingObj() + } allLogs := make([]*types.Log, 0) for _, r := range work.receipts { @@ -162,10 +167,24 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay work.header.RequestsHash = &reqHash } - block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) + // set the block access list on the body after the block has finished executing + // but before the header hash is computed (in FinalizeAndAssemble). + // + // I considered trying to instantiate the beacon consensus engine with a tracer. + // however, the BAL tracer instance is used once per block, while the engine object + // lives for the entire time the client is running. + onBlockFinalization := func() { + if miner.chainConfig.IsAmsterdam(work.header.Number, work.header.Time) { + work.alTracer.OnBlockFinalization() + body.AccessList = work.alTracer.AccessList().ToEncodingObj() + } + } + + block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts, onBlockFinalization) if err != nil { return &newPayloadResult{err: err} } + return &newPayloadResult{ block: block, fees: totalFees(block, work.receipts), @@ -256,13 +275,16 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir if miner.chainConfig.IsPrague(header.Number, header.Time) { core.ProcessParentBlockHash(header.ParentHash, env.evm) } + if miner.chainConfig.IsAmsterdam(header.Number, header.Time) { + env.alTracer.OnPreTxExecutionDone() + } return env, nil } // makeEnv creates a new environment for the sealing block. func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address, witness bool) (*environment, error) { // Retrieve the parent state to execute on top. - state, err := miner.chain.StateAt(parent.Root) + sdb, err := miner.chain.StateAt(parent.Root) if err != nil { return nil, err } @@ -271,17 +293,27 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase if err != nil { return nil, err } - state.StartPrefetcher("miner", bundle, nil) + sdb.StartPrefetcher("miner", bundle, nil) + } + var alTracer *core.BlockAccessListTracer + var hooks *tracing.Hooks + var hookedState vm.StateDB = sdb + var vmConfig vm.Config + if miner.chainConfig.IsAmsterdam(header.Number, header.Time) { + alTracer, hooks = core.NewBlockAccessListTracer() + hookedState = state.NewHookedState(sdb, hooks) + vmConfig.Tracer = hooks } // Note the passed coinbase may be different with header.Coinbase. return &environment{ signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), - state: state, + state: sdb, size: uint64(header.Size()), coinbase: coinbase, header: header, - witness: state.Witness(), - evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}), + witness: sdb.Witness(), + evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), hookedState, miner.chainConfig, vmConfig), + alTracer: alTracer, }, nil } diff --git a/params/config.go b/params/config.go index e796d75535c..294100ca465 100644 --- a/params/config.go +++ b/params/config.go @@ -1007,9 +1007,11 @@ func (c *ChainConfig) CheckConfigForkOrder() error { } if cur.timestamp != nil { // If the fork is configured, a blob schedule must be defined for it. - if cur.config == nil { - return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", cur.name) - } + /* + if cur.config == nil { + return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", cur.name) + } + */ } } return nil @@ -1164,6 +1166,9 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork { // BlobConfig returns the blob config associated with the provided fork. func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig { switch fork { + case forks.Amsterdam: + // TODO: (????) + return c.BlobScheduleConfig.BPO2 case forks.BPO5: return c.BlobScheduleConfig.BPO5 case forks.BPO4: @@ -1209,6 +1214,8 @@ func (c *ChainConfig) ActiveSystemContracts(time uint64) map[string]common.Addre // the fork isn't defined or isn't a time-based fork. func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 { switch { + case fork == forks.Amsterdam: + return c.AmsterdamTime case fork == forks.BPO5: return c.BPO5Time case fork == forks.BPO4: diff --git a/tests/block_test.go b/tests/block_test.go index c718b304b6f..9bc77b4c905 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -18,6 +18,7 @@ package tests import ( "math/rand" + "path/filepath" "testing" "github.com/ethereum/go-ethereum/common" @@ -67,13 +68,119 @@ func TestBlockchain(t *testing.T) { bt.skipLoad(`.*\.meta/.*`) bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { - execBlockTest(t, bt, test) + execBlockTest(t, bt, test, false) }) // There is also a LegacyTests folder, containing blockchain tests generated // prior to Istanbul. However, they are all derived from GeneralStateTests, // which run natively, so there's no reason to run them here. } +func TestBlockchainBAL(t *testing.T) { + bt := new(testMatcher) + + // We are running most of GeneralStatetests to tests witness support, even + // though they are ran as state tests too. Still, the performance tests are + // less about state andmore about EVM number crunching, so skip those. + bt.skipLoad(`^GeneralStateTests/VMTests/vmPerformance`) + + // Skip random failures due to selfish mining test + bt.skipLoad(`.*bcForgedTest/bcForkUncle\.json`) + + // Slow tests + bt.slow(`.*bcExploitTest/DelegateCallSpam.json`) + bt.slow(`.*bcExploitTest/ShanghaiLove.json`) + bt.slow(`.*bcExploitTest/SuicideIssue.json`) + bt.slow(`.*/bcForkStressTest/`) + bt.slow(`.*/bcGasPricerTest/RPC_API_Test.json`) + bt.slow(`.*/bcWalletTest/`) + + // Very slow test + bt.skipLoad(`.*/stTimeConsuming/.*`) + // test takes a lot for time and goes easily OOM because of sha3 calculation on a huge range, + // using 4.6 TGas + bt.skipLoad(`.*randomStatetest94.json.*`) + + // After the merge we would accept side chains as canonical even if they have lower td + bt.skipLoad(`.*bcMultiChainTest/ChainAtoChainB_difficultyB.json`) + bt.skipLoad(`.*bcMultiChainTest/CallContractFromNotBestBlock.json`) + bt.skipLoad(`.*bcTotalDifficultyTest/uncleBlockAtBlock3afterBlock4.json`) + bt.skipLoad(`.*bcTotalDifficultyTest/lotsOfBranchesOverrideAtTheMiddle.json`) + bt.skipLoad(`.*bcTotalDifficultyTest/sideChainWithMoreTransactions.json`) + bt.skipLoad(`.*bcForkStressTest/ForkStressTest.json`) + bt.skipLoad(`.*bcMultiChainTest/lotsOfLeafs.json`) + bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain.json`) + bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain2.json`) + + // With chain history removal, TDs become unavailable, this transition tests based on TTD are unrunnable + bt.skipLoad(`.*bcArrowGlacierToParis/powToPosBlockRejection.json`) + + // This directory contains no test. + bt.skipLoad(`.*\.meta/.*`) + + bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { + config, ok := Forks[test.json.Network] + if !ok { + t.Fatalf("unsupported fork: %s\n", test.json.Network) + } + gspec := test.genesis(config) + // skip any tests which are not past the cancun fork (selfdestruct removal) + if gspec.Config.CancunTime == nil || *gspec.Config.CancunTime != 0 { + return + } + execBlockTest(t, bt, test, true) + }) + // There is also a LegacyTests folder, containing blockchain tests generated + // prior to Istanbul. However, they are all derived from GeneralStateTests, + // which run natively, so there's no reason to run them here. +} + +// TestExecutionSpecBlocktests runs the test fixtures from execution-spec-tests. +// TODO: rename this to reflect that it tests creating/verifying BALs on pre-amsterdam tests +func TestExecutionSpecBlocktestsBAL(t *testing.T) { + if !common.FileExist(executionSpecBlockchainTestDir) { + t.Skipf("directory %s does not exist", executionSpecBlockchainTestDir) + } + bt := new(testMatcher) + + bt.skipLoad(".*prague/eip7251_consolidations/contract_deployment/system_contract_deployment.json") + bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/contract_deployment/system_contract_deployment.json") + + bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { + config, ok := Forks[test.json.Network] + if !ok { + t.Fatalf("unsupported fork: %s\n", test.json.Network) + } + gspec := test.genesis(config) + // skip any tests which are not past the cancun fork (selfdestruct removal) + if gspec.Config.CancunTime == nil || *gspec.Config.CancunTime != 0 { + return + } + execBlockTest(t, bt, test, true) + }) +} + +func TestExecutionSpecBlocktestsAmsterdam(t *testing.T) { + var executionSpecAmsterdamBlockchainTestDir = filepath.Join(".", "fixtures-amsterdam-bal", "blockchain_tests") + if !common.FileExist(executionSpecAmsterdamBlockchainTestDir) { + t.Skipf("directory %s does not exist", executionSpecAmsterdamBlockchainTestDir) + } + bt := new(testMatcher) + + bt.walk(t, executionSpecAmsterdamBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { + config, ok := Forks[test.json.Network] + if !ok { + t.Fatalf("unsupported fork: %s\n", test.json.Network) + } + gspec := test.genesis(config) + // skip any tests which are not past the cancun fork (selfdestruct removal) + if gspec.Config.CancunTime == nil || *gspec.Config.CancunTime != 0 { + return + } + // TODO: skip any tests that aren't amsterdam + execBlockTest(t, bt, test, false) + }) +} + // TestExecutionSpecBlocktests runs the test fixtures from execution-spec-tests. func TestExecutionSpecBlocktests(t *testing.T) { if !common.FileExist(executionSpecBlockchainTestDir) { @@ -86,11 +193,11 @@ func TestExecutionSpecBlocktests(t *testing.T) { bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/test_system_contract_deployment.json") bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { - execBlockTest(t, bt, test) + execBlockTest(t, bt, test, false) }) } -func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) { +func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest, buildAndVerifyBAL bool) { // Define all the different flag combinations we should run the tests with, // picking only one for short tests. // @@ -104,9 +211,11 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) { snapshotConf = []bool{snapshotConf[rand.Int()%2]} dbschemeConf = []string{dbschemeConf[rand.Int()%2]} } + for _, snapshot := range snapshotConf { for _, dbscheme := range dbschemeConf { - if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil { + //tracer := logger.NewJSONLogger(&logger.Config{}, os.Stdout) + if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, false, buildAndVerifyBAL, 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 3b88753b1c8..eb912a5f219 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -22,6 +22,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/core/types/bal" stdmath "math" "math/big" "os" @@ -71,6 +72,7 @@ type btBlock struct { ExpectException string Rlp string UncleHeaders []*btHeader + AccessList *bal.BlockAccessList `json:"blockAccessList,omitempty"` } //go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go @@ -97,6 +99,7 @@ type btHeader struct { BlobGasUsed *uint64 ExcessBlobGas *uint64 ParentBeaconBlockRoot *common.Hash + BlockAccessListHash *common.Hash } type btHeaderMarshaling struct { @@ -111,11 +114,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) { - config, ok := Forks[t.json.Network] - if !ok { - return UnsupportedForkError{t.json.Network} - } +func (t *BlockTest) createTestBlockChain(config *params.ChainConfig, snapshotter bool, scheme string, witness, createAndVerifyBAL bool, tracer *tracing.Hooks) (*core.BlockChain, error) { // import pre accounts & construct test genesis block & state root var ( db = rawdb.NewMemoryDatabase() @@ -128,7 +127,6 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } else { tconf.HashDB = hashdb.Defaults } - // Commit genesis state gspec := t.genesis(config) // if ttd is not specified, set an arbitrary huge value @@ -138,15 +136,15 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t triedb := triedb.NewDatabase(db, tconf) gblock, err := gspec.Commit(db, triedb) if err != nil { - return err + return nil, err } triedb.Close() // close the db to prevent memory leak if gblock.Hash() != t.json.Genesis.Hash { - return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) + return nil, fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) } if gblock.Root() != t.json.Genesis.StateRoot { - return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) + return nil, fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) } // Wrap the original engine within the beacon-engine engine := beacon.New(ethash.NewFaker()) @@ -160,12 +158,28 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t Tracer: tracer, StatelessSelfValidation: witness, }, + NoPrefetch: true, + EnableBALForTesting: createAndVerifyBAL, } if snapshotter { options.SnapshotLimit = 1 options.SnapshotWait = true } chain, err := core.NewBlockChain(db, gspec, engine, options) + if err != nil { + return nil, err + } + return chain, nil +} + +func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, createAndVerifyBAL bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { + config, ok := Forks[t.json.Network] + if !ok { + return UnsupportedForkError{t.json.Network} + } + // import pre accounts & construct test genesis block & state root + + chain, err := t.createTestBlockChain(config, snapshotter, scheme, witness, createAndVerifyBAL, tracer) if err != nil { return err } @@ -199,25 +213,69 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } } } - return t.validateImportedHeaders(chain, validBlocks) + err = t.validateImportedHeaders(chain, validBlocks) + if err != nil { + return err + } + + if createAndVerifyBAL { + newChain, _ := t.createTestBlockChain(config, snapshotter, scheme, witness, createAndVerifyBAL, tracer) + defer newChain.Stop() + + var blocksWithBAL types.Blocks + for i := uint64(1); i <= chain.CurrentBlock().Number.Uint64(); i++ { + block := chain.GetBlockByNumber(i) + if block.Body().AccessList == nil { + return fmt.Errorf("block %d missing BAL", block.NumberU64()) + } + blocksWithBAL = append(blocksWithBAL, block) + } + + amt, err := newChain.InsertChain(blocksWithBAL) + if err != nil { + return err + } + _ = amt + newDB, err := newChain.State() + if err != nil { + return err + } + if err = t.validatePostState(newDB); err != nil { + return fmt.Errorf("post state validation failed: %v", err) + } + // Cross-check the snapshot-to-hash against the trie hash + if snapshotter { + if newChain.Snapshots() != nil { + if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil { + return err + } + } + } + err = t.validateImportedHeaders(newChain, validBlocks) + if err != nil { + return err + } + } + return nil } func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { return &core.Genesis{ - Config: config, - Nonce: t.json.Genesis.Nonce.Uint64(), - Timestamp: t.json.Genesis.Timestamp, - ParentHash: t.json.Genesis.ParentHash, - ExtraData: t.json.Genesis.ExtraData, - GasLimit: t.json.Genesis.GasLimit, - GasUsed: t.json.Genesis.GasUsed, - Difficulty: t.json.Genesis.Difficulty, - Mixhash: t.json.Genesis.MixHash, - Coinbase: t.json.Genesis.Coinbase, - Alloc: t.json.Pre, - BaseFee: t.json.Genesis.BaseFeePerGas, - BlobGasUsed: t.json.Genesis.BlobGasUsed, - ExcessBlobGas: t.json.Genesis.ExcessBlobGas, + Config: config, + Nonce: t.json.Genesis.Nonce.Uint64(), + Timestamp: t.json.Genesis.Timestamp, + ParentHash: t.json.Genesis.ParentHash, + ExtraData: t.json.Genesis.ExtraData, + GasLimit: t.json.Genesis.GasLimit, + GasUsed: t.json.Genesis.GasUsed, + Difficulty: t.json.Genesis.Difficulty, + Mixhash: t.json.Genesis.MixHash, + Coinbase: t.json.Genesis.Coinbase, + Alloc: t.json.Pre, + BaseFee: t.json.Genesis.BaseFeePerGas, + BlobGasUsed: t.json.Genesis.BlobGasUsed, + ExcessBlobGas: t.json.Genesis.ExcessBlobGas, + BlockAccessListHash: t.json.Genesis.BlockAccessListHash, } } diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go index 80ad89e03bf..6e6562535b2 100644 --- a/tests/gen_btheader.go +++ b/tests/gen_btheader.go @@ -38,6 +38,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) { BlobGasUsed *math.HexOrDecimal64 ExcessBlobGas *math.HexOrDecimal64 ParentBeaconBlockRoot *common.Hash + BlockAccessListHash *common.Hash } var enc btHeader enc.Bloom = b.Bloom @@ -61,6 +62,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) { enc.BlobGasUsed = (*math.HexOrDecimal64)(b.BlobGasUsed) enc.ExcessBlobGas = (*math.HexOrDecimal64)(b.ExcessBlobGas) enc.ParentBeaconBlockRoot = b.ParentBeaconBlockRoot + enc.BlockAccessListHash = b.BlockAccessListHash return json.Marshal(&enc) } @@ -88,6 +90,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error { BlobGasUsed *math.HexOrDecimal64 ExcessBlobGas *math.HexOrDecimal64 ParentBeaconBlockRoot *common.Hash + BlockAccessListHash *common.Hash } var dec btHeader if err := json.Unmarshal(input, &dec); err != nil { @@ -156,5 +159,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error { if dec.ParentBeaconBlockRoot != nil { b.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot } + if dec.BlockAccessListHash != nil { + b.BlockAccessListHash = dec.BlockAccessListHash + } return nil } diff --git a/tests/init.go b/tests/init.go index 705e929ae9d..2f1dfaba30d 100644 --- a/tests/init.go +++ b/tests/init.go @@ -493,6 +493,38 @@ var Forks = map[string]*params.ChainConfig{ BPO1: bpo1BlobConfig, }, }, + "Amsterdam": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + AmsterdamTime: u64(0), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, + }, + }, "OsakaToBPO1AtTime15k": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 7c7bd184bf8..d2e503e15f0 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -210,6 +210,29 @@ func (t *StateTrie) UpdateStorage(_ common.Address, key, value []byte) error { return nil } +// UpdateStorageBatch attempts to update a list storages in the batch manner. +func (t *StateTrie) UpdateStorageBatch(_ common.Address, keys [][]byte, values [][]byte) error { + var ( + hkeys = make([][]byte, 0, len(keys)) + evals = make([][]byte, 0, len(values)) + ) + for _, key := range keys { + hk := crypto.Keccak256(key) + if t.preimages != nil { + t.secKeyCache[common.Hash(hk)] = key + } + hkeys = append(hkeys, hk) + } + for _, val := range values { + data, err := rlp.EncodeToBytes(val) + if err != nil { + return err + } + evals = append(evals, data) + } + return t.trie.UpdateBatch(hkeys, evals) +} + // UpdateAccount will abstract the write of an account to the secure trie. func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccount, _ int) error { hk := crypto.Keccak256(address.Bytes()) @@ -226,6 +249,29 @@ func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccoun return nil } +// UpdateAccountBatch attempts to update a list accounts in the batch manner. +func (t *StateTrie) UpdateAccountBatch(addresses []common.Address, accounts []*types.StateAccount, _ []int) error { + var ( + hkeys = make([][]byte, 0, len(addresses)) + values = make([][]byte, 0, len(accounts)) + ) + for _, addr := range addresses { + hk := crypto.Keccak256(addr.Bytes()) + if t.preimages != nil { + t.secKeyCache[common.Hash(hk)] = addr.Bytes() + } + hkeys = append(hkeys, hk) + } + for _, acc := range accounts { + data, err := rlp.EncodeToBytes(acc) + if err != nil { + return err + } + values = append(values, data) + } + return t.trie.UpdateBatch(hkeys, values) +} + func (t *StateTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error { return nil } diff --git a/trie/tracer.go b/trie/tracer.go index 04122d1384f..042fa468bfa 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -33,12 +33,10 @@ import ( // while the latter is inserted/deleted in order to follow the rule of trie. // This tool can track all of them no matter the node is embedded in its // parent or not, but valueNode is never tracked. -// -// Note opTracer is not thread-safe, callers should be responsible for handling -// the concurrency issues by themselves. type opTracer struct { inserts map[string]struct{} deletes map[string]struct{} + lock sync.RWMutex } // newOpTracer initializes the tracer for capturing trie changes. @@ -53,6 +51,9 @@ func newOpTracer() *opTracer { // in the deletion set (resurrected node), then just wipe it from // the deletion set as it's "untouched". func (t *opTracer) onInsert(path []byte) { + t.lock.Lock() + defer t.lock.Unlock() + if _, present := t.deletes[string(path)]; present { delete(t.deletes, string(path)) return @@ -64,6 +65,9 @@ func (t *opTracer) onInsert(path []byte) { // in the addition set, then just wipe it from the addition set // as it's untouched. func (t *opTracer) onDelete(path []byte) { + t.lock.Lock() + defer t.lock.Unlock() + if _, present := t.inserts[string(path)]; present { delete(t.inserts, string(path)) return @@ -73,12 +77,18 @@ func (t *opTracer) onDelete(path []byte) { // reset clears the content tracked by tracer. func (t *opTracer) reset() { + t.lock.Lock() + defer t.lock.Unlock() + clear(t.inserts) clear(t.deletes) } // copy returns a deep copied tracer instance. func (t *opTracer) copy() *opTracer { + t.lock.RLock() + defer t.lock.RUnlock() + return &opTracer{ inserts: maps.Clone(t.inserts), deletes: maps.Clone(t.deletes), @@ -87,6 +97,9 @@ func (t *opTracer) copy() *opTracer { // deletedList returns a list of node paths which are deleted from the trie. func (t *opTracer) deletedList() [][]byte { + t.lock.RLock() + defer t.lock.RUnlock() + paths := make([][]byte, 0, len(t.deletes)) for path := range t.deletes { paths = append(paths, []byte(path)) diff --git a/trie/transition.go b/trie/transition.go index c6eecd39376..a890e62102b 100644 --- a/trie/transition.go +++ b/trie/transition.go @@ -45,6 +45,14 @@ func NewTransitionTrie(base *SecureTrie, overlay *VerkleTrie, st bool) *Transiti } } +func (t *TransitionTrie) UpdateAccountBatch(addresses []common.Address, accounts []*types.StateAccount, _ []int) error { + panic("not implemented") +} + +func (t *TransitionTrie) UpdateStorageBatch(_ common.Address, keys [][]byte, values [][]byte) error { + panic("not implemented") +} + // Base returns the base trie. func (t *TransitionTrie) Base() *SecureTrie { return t.base diff --git a/trie/trie.go b/trie/trie.go index 1ef2c2f1a66..1f117e1e42d 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -480,6 +480,72 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error } } +// UpdateBatch updates a batch of entries concurrently. +func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error { + // Short circuit if the trie is already committed and unusable. + if t.committed { + return ErrCommitted + } + if len(keys) != len(values) { + return fmt.Errorf("keys and values length mismatch: %d != %d", len(keys), len(values)) + } + // Insert the entries sequentially if there are not too many + // trie nodes in the trie. + fn, ok := t.root.(*fullNode) + + if !ok || len(keys) < 4 { // TODO(rjl493456442) the parallelism threshold should be twisted + for i, key := range keys { + err := t.Update(key, values[i]) + if err != nil { + return err + } + } + return nil + } + var ( + ikeys = make(map[byte][][]byte) + ivals = make(map[byte][][]byte) + eg errgroup.Group + ) + for i, key := range keys { + hkey := keybytesToHex(key) + ikeys[hkey[0]] = append(ikeys[hkey[0]], hkey) + ivals[hkey[0]] = append(ivals[hkey[0]], values[i]) + } + if len(keys) > 0 { + fn.flags = t.newFlag() + } + for p, k := range ikeys { + pos := p + ks := k + eg.Go(func() error { + vs := ivals[pos] + for i, k := range ks { + if len(vs[i]) != 0 { + _, n, err := t.insert(fn.Children[pos], []byte{pos}, k[1:], valueNode(vs[i])) + if err != nil { + return err + } + fn.Children[pos] = n + } else { + _, n, err := t.delete(fn.Children[pos], []byte{pos}, k[1:]) + if err != nil { + return err + } + fn.Children[pos] = n + } + } + return nil + }) + } + if err := eg.Wait(); err != nil { + return err + } + t.unhashed += len(keys) + t.uncommitted += len(keys) + return nil +} + // MustDelete is a wrapper of Delete and will omit any encountered error but // just print out an error message. func (t *Trie) MustDelete(key []byte) { diff --git a/trie/trie_test.go b/trie/trie_test.go index b8b8edb33e8..19403b48c41 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -1500,82 +1500,56 @@ func testTrieCopyNewTrie(t *testing.T, entries []kv) { } } -// goos: darwin -// goarch: arm64 -// pkg: github.com/ethereum/go-ethereum/trie -// cpu: Apple M1 Pro -// BenchmarkTriePrefetch -// BenchmarkTriePrefetch-8 9961 100706 ns/op -func BenchmarkTriePrefetch(b *testing.B) { - db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) - tr := NewEmpty(db) - vals := make(map[string]*kv) - for i := 0; i < 3000; i++ { - value := &kv{ - k: randBytes(32), - v: randBytes(20), - t: false, - } - tr.MustUpdate(value.k, value.v) - vals[string(value.k)] = value - } - root, nodes := tr.Commit(false) - db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) - b.ResetTimer() +func TestUpdateBatch(t *testing.T) { + testUpdateBatch(t, []kv{ + {k: []byte("do"), v: []byte("verb")}, + {k: []byte("ether"), v: []byte("wookiedoo")}, + {k: []byte("horse"), v: []byte("stallion")}, + {k: []byte("shaman"), v: []byte("horse")}, + {k: []byte("doge"), v: []byte("coin")}, + {k: []byte("dog"), v: []byte("puppy")}, + }) - for i := 0; i < b.N; i++ { - tr, err := New(TrieID(root), db) - if err != nil { - b.Fatalf("Failed to open the trie") - } - var keys [][]byte - for k := range vals { - keys = append(keys, []byte(k)) - if len(keys) > 64 { - break - } - } - tr.Prefetch(keys) + var entries []kv + for i := 0; i < 256; i++ { + entries = append(entries, kv{k: testrand.Bytes(32), v: testrand.Bytes(32)}) } + testUpdateBatch(t, entries) } -// goos: darwin -// goarch: arm64 -// pkg: github.com/ethereum/go-ethereum/trie -// cpu: Apple M1 Pro -// BenchmarkTrieSeqPrefetch -// BenchmarkTrieSeqPrefetch-8 12879 96710 ns/op -func BenchmarkTrieSeqPrefetch(b *testing.B) { - db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) - tr := NewEmpty(db) - vals := make(map[string]*kv) - for i := 0; i < 3000; i++ { - value := &kv{ - k: randBytes(32), - v: randBytes(20), - t: false, - } - tr.MustUpdate(value.k, value.v) - vals[string(value.k)] = value +func testUpdateBatch(t *testing.T, entries []kv) { + var ( + base = NewEmpty(nil) + keys [][]byte + vals [][]byte + ) + for _, entry := range entries { + base.Update(entry.k, entry.v) + keys = append(keys, entry.k) + vals = append(vals, entry.v) + } + for i := 0; i < 10; i++ { + k, v := testrand.Bytes(32), testrand.Bytes(32) + base.Update(k, v) + keys = append(keys, k) + vals = append(vals, v) } - root, nodes := tr.Commit(false) - db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - tr, err := New(TrieID(root), db) - if err != nil { - b.Fatalf("Failed to open the trie") - } - var keys [][]byte - for k := range vals { - keys = append(keys, []byte(k)) - if len(keys) > 64 { - break - } - } - for _, k := range keys { - tr.Get(k) + cmp := NewEmpty(nil) + if err := cmp.UpdateBatch(keys, vals); err != nil { + t.Fatalf("Failed to update batch, %v", err) + } + + // Traverse the original tree, the changes made on the copy one shouldn't + // affect the old one + for _, key := range keys { + v1, _ := base.Get(key) + v2, _ := cmp.Get(key) + if !bytes.Equal(v1, v2) { + t.Errorf("Unexpected data, key: %v, want: %v, got: %v", key, v1, v2) } } + if base.Hash() != cmp.Hash() { + t.Errorf("Hash mismatch: want %x, got %x", base.Hash(), cmp.Hash()) + } } diff --git a/trie/verkle.go b/trie/verkle.go index 186ac1f642b..900cdaf7989 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -177,6 +177,22 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, return nil } +// UpdateAccountBatch attempts to update a list accounts in the batch manner. +func (t *VerkleTrie) UpdateAccountBatch(addresses []common.Address, accounts []*types.StateAccount, codeLens []int) error { + if len(addresses) != len(accounts) { + return fmt.Errorf("address and accounts length mismatch: %d != %d", len(addresses), len(accounts)) + } + if len(addresses) != len(codeLens) { + return fmt.Errorf("address and code length mismatch: %d != %d", len(addresses), len(codeLens)) + } + for i, addr := range addresses { + if err := t.UpdateAccount(addr, accounts[i], codeLens[i]); err != nil { + return err + } + } + return nil +} + // UpdateStorage implements state.Trie, writing the provided storage slot into // the tree. If the tree is corrupted, an error will be returned. func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error { @@ -191,6 +207,19 @@ func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) er return t.root.Insert(k, v[:], t.nodeResolver) } +// UpdateStorageBatch attempts to update a list storages in the batch manner. +func (t *VerkleTrie) UpdateStorageBatch(address common.Address, keys [][]byte, values [][]byte) error { + if len(keys) != len(values) { + return fmt.Errorf("keys and values length mismatch: %d != %d", len(keys), len(values)) + } + for i, key := range keys { + if err := t.UpdateStorage(address, key, values[i]); err != nil { + return err + } + } + return nil +} + // DeleteAccount leaves the account untouched, as no account deletion can happen // in verkle. // There is a special corner case, in which an account that is prefunded, CREATE2-d